mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Shape Keys: Apply to Basis operator
As described in #150299, this operator applies the deformations of the selected shape keys to the basis key and then removes them. Pull Request: https://projects.blender.org/blender/blender/pulls/150540
This commit is contained in:
parent
7022cfd007
commit
56049e5a23
7 changed files with 168 additions and 16 deletions
|
|
@ -75,6 +75,7 @@ class MESH_MT_shape_key_context_menu(Menu):
|
|||
layout.operator("object.shape_key_lock", icon='UNLOCKED', text="Unlock All").action = 'UNLOCK'
|
||||
layout.separator()
|
||||
layout.operator("object.shape_key_make_basis", text="Make Basis")
|
||||
layout.operator("object.shape_key_apply_to_basis", text="Apply to Basis")
|
||||
layout.separator()
|
||||
props = layout.operator("object.shape_key_remove", text="Apply All")
|
||||
props.all = True
|
||||
|
|
@ -90,6 +91,7 @@ class MESH_MT_shape_key_tree_context_menu(Menu):
|
|||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
layout.operator("object.shape_key_make_basis", text="Make Basis")
|
||||
layout.operator("object.shape_key_apply_to_basis", text="Apply to Basis")
|
||||
layout.operator("object.shape_key_copy", icon='DUPLICATE', text="Duplicate")
|
||||
layout.separator()
|
||||
layout.operator("object.shape_key_move", icon='TRIA_UP_BAR', text="Move After Basis").type = 'TOP'
|
||||
|
|
|
|||
|
|
@ -52,9 +52,14 @@ void key_curve_normal_weights(float t, float data[4], KeyInterpolationType type)
|
|||
* Returns key coordinates (+ tilt) when key applied, NULL otherwise.
|
||||
*
|
||||
* \param obdata: if given, also update that geometry with the result of the shape keys evaluation.
|
||||
* \param keys_to_process: If provided, only evaluate the keys at indices set to true.
|
||||
*/
|
||||
float *BKE_key_evaluate_object_ex(
|
||||
Object *ob, int *r_totelem, float *arr, size_t arr_size, ID *obdata);
|
||||
float *BKE_key_evaluate_object_ex(Object *ob,
|
||||
int *r_totelem,
|
||||
float *arr,
|
||||
size_t arr_size,
|
||||
std::optional<Span<bool>> keys_to_process,
|
||||
ID *obdata);
|
||||
float *BKE_key_evaluate_object(Object *ob, int *r_totelem);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -674,6 +674,7 @@ static void copy_key_float3_weighted(const int vertex_count,
|
|||
*/
|
||||
static void key_evaluate_relative_float3(Key *key,
|
||||
KeyBlock *active_keyblock,
|
||||
const std::optional<Span<bool>> keys_to_process,
|
||||
const int vertex_count,
|
||||
float **per_keyblock_weights,
|
||||
float *target_data)
|
||||
|
|
@ -688,6 +689,9 @@ static void key_evaluate_relative_float3(Key *key,
|
|||
if (&kb == key->refkey) {
|
||||
continue;
|
||||
}
|
||||
if (keys_to_process && !(*keys_to_process)[enumerator.first]) {
|
||||
continue;
|
||||
}
|
||||
/* No difference in vertex count allowed. */
|
||||
if (kb.flag & KEYBLOCK_MUTE || kb.totelem != vertex_count) {
|
||||
continue;
|
||||
|
|
@ -892,7 +896,11 @@ static void keyblock_free_per_block_weights(Key *key,
|
|||
MEM_delete(per_keyblock_weights);
|
||||
}
|
||||
|
||||
static void do_mesh_key(Object *ob, Key *key, char *out, const int tot)
|
||||
static void do_mesh_key(Object *ob,
|
||||
Key *key,
|
||||
const std::optional<Span<bool>> keys_to_process,
|
||||
char *out,
|
||||
const int tot)
|
||||
{
|
||||
KeyBlock *actkb = BKE_keyblock_from_object(ob);
|
||||
|
||||
|
|
@ -901,7 +909,7 @@ static void do_mesh_key(Object *ob, Key *key, char *out, const int tot)
|
|||
float **per_keyblock_weights;
|
||||
per_keyblock_weights = keyblock_get_per_block_weights(ob, key, &cache);
|
||||
key_evaluate_relative_float3(
|
||||
key, actkb, tot, per_keyblock_weights, reinterpret_cast<float *>(out));
|
||||
key, actkb, keys_to_process, tot, per_keyblock_weights, reinterpret_cast<float *>(out));
|
||||
keyblock_free_per_block_weights(key, per_keyblock_weights, &cache);
|
||||
}
|
||||
else {
|
||||
|
|
@ -920,12 +928,17 @@ static void do_mesh_key(Object *ob, Key *key, char *out, const int tot)
|
|||
}
|
||||
}
|
||||
|
||||
static void do_curve_key(Object *ob, Key *key, char *out, const int tot)
|
||||
static void do_curve_key(Object *ob,
|
||||
Key *key,
|
||||
const std::optional<Span<bool>> keys_to_process,
|
||||
char *out,
|
||||
const int tot)
|
||||
{
|
||||
KeyBlock *actkb = BKE_keyblock_from_object(ob);
|
||||
|
||||
if (key->type == KEY_RELATIVE) {
|
||||
key_evaluate_relative_float3(key, actkb, tot, nullptr, reinterpret_cast<float *>(out));
|
||||
key_evaluate_relative_float3(
|
||||
key, actkb, keys_to_process, tot, nullptr, reinterpret_cast<float *>(out));
|
||||
}
|
||||
else {
|
||||
const float ctime_scaled = key->ctime / 100.0f;
|
||||
|
|
@ -943,7 +956,11 @@ static void do_curve_key(Object *ob, Key *key, char *out, const int tot)
|
|||
}
|
||||
}
|
||||
|
||||
static void do_latt_key(Object *ob, Key *key, char *out, const int tot)
|
||||
static void do_latt_key(Object *ob,
|
||||
Key *key,
|
||||
const std::optional<Span<bool>> keys_to_process,
|
||||
char *out,
|
||||
const int tot)
|
||||
{
|
||||
Lattice *lt = id_cast<Lattice *>(ob->data);
|
||||
KeyBlock *actkb = BKE_keyblock_from_object(ob);
|
||||
|
|
@ -952,7 +969,7 @@ static void do_latt_key(Object *ob, Key *key, char *out, const int tot)
|
|||
float **per_keyblock_weights;
|
||||
per_keyblock_weights = keyblock_get_per_block_weights(ob, key, nullptr);
|
||||
key_evaluate_relative_float3(
|
||||
key, actkb, tot, per_keyblock_weights, reinterpret_cast<float *>(out));
|
||||
key, actkb, keys_to_process, tot, per_keyblock_weights, reinterpret_cast<float *>(out));
|
||||
keyblock_free_per_block_weights(key, per_keyblock_weights, nullptr);
|
||||
}
|
||||
else {
|
||||
|
|
@ -982,8 +999,12 @@ static void keyblock_data_convert_to_curve(const float *fp,
|
|||
ListBaseT<Nurb> *nurb,
|
||||
const int totpoint);
|
||||
|
||||
float *BKE_key_evaluate_object_ex(
|
||||
Object *ob, int *r_totelem, float *arr, size_t arr_size, ID *obdata)
|
||||
float *BKE_key_evaluate_object_ex(Object *ob,
|
||||
int *r_totelem,
|
||||
float *arr,
|
||||
size_t arr_size,
|
||||
const std::optional<Span<bool>> keys_to_process,
|
||||
ID *obdata)
|
||||
{
|
||||
Key *key = BKE_key_from_object(ob);
|
||||
KeyBlock *actkb = BKE_keyblock_from_object(ob);
|
||||
|
|
@ -1058,16 +1079,16 @@ float *BKE_key_evaluate_object_ex(
|
|||
}
|
||||
else {
|
||||
if (ob->type == OB_MESH) {
|
||||
do_mesh_key(ob, key, out, tot);
|
||||
do_mesh_key(ob, key, keys_to_process, out, tot);
|
||||
}
|
||||
else if (ob->type == OB_LATTICE) {
|
||||
do_latt_key(ob, key, out, tot);
|
||||
do_latt_key(ob, key, keys_to_process, out, tot);
|
||||
}
|
||||
else if (ob->type == OB_CURVES_LEGACY) {
|
||||
do_curve_key(ob, key, out, tot);
|
||||
do_curve_key(ob, key, keys_to_process, out, tot);
|
||||
}
|
||||
else if (ob->type == OB_SURF) {
|
||||
do_curve_key(ob, key, out, tot);
|
||||
do_curve_key(ob, key, keys_to_process, out, tot);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1108,7 +1129,7 @@ float *BKE_key_evaluate_object_ex(
|
|||
|
||||
float *BKE_key_evaluate_object(Object *ob, int *r_totelem)
|
||||
{
|
||||
return BKE_key_evaluate_object_ex(ob, r_totelem, nullptr, 0, nullptr);
|
||||
return BKE_key_evaluate_object_ex(ob, r_totelem, nullptr, 0, std::nullopt, nullptr);
|
||||
}
|
||||
|
||||
int BKE_keyblock_element_count_from_shape(const Key *key, const int shape_index)
|
||||
|
|
|
|||
|
|
@ -330,6 +330,7 @@ void OBJECT_OT_shape_key_mirror(wmOperatorType *ot);
|
|||
void OBJECT_OT_shape_key_move(wmOperatorType *ot);
|
||||
void OBJECT_OT_shape_key_lock(wmOperatorType *ot);
|
||||
void OBJECT_OT_shape_key_make_basis(wmOperatorType *ot);
|
||||
void OBJECT_OT_shape_key_apply_to_basis(wmOperatorType *ot);
|
||||
|
||||
/* `object_collection.cc` */
|
||||
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ void operatortypes_object()
|
|||
WM_operatortype_append(OBJECT_OT_shape_key_move);
|
||||
WM_operatortype_append(OBJECT_OT_shape_key_lock);
|
||||
WM_operatortype_append(OBJECT_OT_shape_key_make_basis);
|
||||
WM_operatortype_append(OBJECT_OT_shape_key_apply_to_basis);
|
||||
|
||||
WM_operatortype_append(OBJECT_OT_collection_add);
|
||||
WM_operatortype_append(OBJECT_OT_collection_link);
|
||||
|
|
|
|||
|
|
@ -418,7 +418,7 @@ static wmOperatorStatus shape_key_remove_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
|
||||
if (RNA_boolean_get(op->ptr, "apply_mix")) {
|
||||
float *arr = BKE_key_evaluate_object_ex(ob, nullptr, nullptr, 0, ob->data);
|
||||
float *arr = BKE_key_evaluate_object_ex(ob, nullptr, nullptr, 0, std::nullopt, ob->data);
|
||||
MEM_delete(arr);
|
||||
}
|
||||
changed = BKE_object_shapekey_free(bmain, ob);
|
||||
|
|
@ -921,4 +921,125 @@ void OBJECT_OT_shape_key_make_basis(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shape Key Make Basis Operator
|
||||
* \{ */
|
||||
|
||||
static bool shape_key_apply_to_basis_poll(bContext *C)
|
||||
{
|
||||
if (!shape_key_exists_poll(C)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object *ob = context_object(C);
|
||||
if (ob->type != OB_MESH) {
|
||||
return false;
|
||||
}
|
||||
/* 0 = nothing active, 1 = basis key active. */
|
||||
return ob->shapenr > 1;
|
||||
}
|
||||
|
||||
static void add_arrays(const MutableSpan<float3> a, const Span<float3> b)
|
||||
{
|
||||
BLI_assert(a.size() == b.size());
|
||||
threading::parallel_for(a.index_range(), 2048, [&](const IndexRange range) {
|
||||
for (const int i : range) {
|
||||
a[i] += b[i];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static wmOperatorStatus shape_key_apply_to_basis_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
Key *key = BKE_key_from_object(ob);
|
||||
KeyBlock *basis_key = static_cast<KeyBlock *>(key->block.first);
|
||||
MutableSpan<float3> basis_data(static_cast<float3 *>(basis_key->data), basis_key->totelem);
|
||||
Mesh &mesh = id_cast<Mesh &>(*ob->data);
|
||||
|
||||
MutableSpan<float3> positions = mesh.vert_positions_for_write();
|
||||
|
||||
int locked_count = 0;
|
||||
Array<bool> keys_to_process(BLI_listbase_count(&key->block), false);
|
||||
for (const auto [i, kb] : key->block.enumerate()) {
|
||||
if (!shape_key_is_selected(*ob, kb, i)) {
|
||||
continue;
|
||||
}
|
||||
if (kb.flag & KEYBLOCK_LOCKED_SHAPE) {
|
||||
locked_count++;
|
||||
continue;
|
||||
}
|
||||
keys_to_process[i] = true;
|
||||
}
|
||||
|
||||
if (locked_count != 0) {
|
||||
BKE_reportf(op->reports, RPT_INFO, "Skipped %d locked shape keys", locked_count);
|
||||
}
|
||||
|
||||
if (!keys_to_process.as_span().contains(true)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
int totelem_dummy;
|
||||
BKE_key_evaluate_object_ex(ob,
|
||||
&totelem_dummy,
|
||||
positions.cast<float>().data(),
|
||||
sizeof(float3) * positions.size(),
|
||||
keys_to_process,
|
||||
nullptr);
|
||||
|
||||
Array<float3> translations(positions.size());
|
||||
for (const int i : positions.index_range()) {
|
||||
translations[i] = positions[i] - basis_data[i];
|
||||
}
|
||||
|
||||
basis_data.copy_from(positions);
|
||||
|
||||
Set<KeyBlock *> processed_keys;
|
||||
for (const auto [i, kb] : key->block.enumerate()) {
|
||||
if (keys_to_process[i]) {
|
||||
processed_keys.add_new(&kb);
|
||||
}
|
||||
}
|
||||
for (KeyBlock *kb : processed_keys) {
|
||||
BKE_object_shapekey_remove(bmain, ob, kb);
|
||||
}
|
||||
|
||||
if (const std::optional<Array<bool>> dependent = BKE_keyblock_get_dependent_keys(key, 0)) {
|
||||
for (const auto [i, kb] : key->block.enumerate()) {
|
||||
if (&kb == basis_key) {
|
||||
continue;
|
||||
}
|
||||
if (!(*dependent)[i]) {
|
||||
continue;
|
||||
}
|
||||
MutableSpan<float3> kb_data(static_cast<float3 *>(kb.data), kb.totelem);
|
||||
add_arrays(kb_data, translations);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make the basis key active. */
|
||||
ob->shapenr = 1;
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void OBJECT_OT_shape_key_apply_to_basis(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Apply to Basis Key";
|
||||
ot->idname = "OBJECT_OT_shape_key_apply_to_basis";
|
||||
ot->description = "Appply deformations of selected shape keys to the basis key, removing them";
|
||||
|
||||
ot->poll = shape_key_apply_to_basis_poll;
|
||||
ot->exec = shape_key_apply_to_basis_exec;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::ed::object
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ static void deform_verts(ModifierData * /*md*/,
|
|||
&deformedVerts_tot,
|
||||
reinterpret_cast<float *>(positions.data()),
|
||||
sizeof(float3) * positions.size(),
|
||||
std::nullopt,
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue