D14823: Adds operator to duplicate the active color attribute layer

Fixes T97706

Adds operator to duplicate the active color attribute layer.
Adds `"Color Attribute Specials"` menu to color attribute ui to access the `"geometry.color_attribute_duplicate"` operator.
Internally adds a function that duplicates a referenced CustomDataLayer
- `BKE_id_attribute_duplicate` mostly copies the existing `BKE_id_attribute_new`
- but gets the type and domain from the referenced layer
- and copies the data from the old layer into the new layer

Reviewed By: Joseph Eagar & Hans Goudey & Julien Kaspar
Differential Revision: https://developer.blender.org/D14823
Ref D14823
This commit is contained in:
Dennis Ranish 2022-06-08 12:10:32 -07:00 committed by Joseph Eagar
parent f69c565a33
commit 9c28f0eb37
Notes: blender-bot 2023-05-29 09:17:12 +02:00
Referenced by issue #97706, Duplicate active color attribute
6 changed files with 129 additions and 3 deletions

View File

@ -64,6 +64,18 @@ class MESH_MT_shape_key_context_menu(Menu):
layout.operator("object.shape_key_move", icon='TRIA_DOWN_BAR', text="Move to Bottom").type = 'BOTTOM'
class MESH_MT_color_attribute_context_menu(Menu):
bl_label = "Color Attribute Specials"
def draw(self, _context):
layout = self.layout
props = layout.operator(
"geometry.color_attribute_duplicate",
icon='DUPLICATE',
)
class MESH_MT_attribute_context_menu(Menu):
bl_label = "Attribute Specials"
@ -660,12 +672,16 @@ class DATA_PT_vertex_colors(DATA_PT_mesh_attributes, Panel):
col.operator("geometry.color_attribute_add", icon='ADD', text="")
col.operator("geometry.color_attribute_remove", icon='REMOVE', text="")
self.draw_attribute_warnings(context, layout)
col.separator()
col.menu("MESH_MT_color_attribute_context_menu", icon='DOWNARROW_HLT', text="")
self.draw_attribute_warnings(context, layout)
classes = (
MESH_MT_vertex_group_context_menu,
MESH_MT_shape_key_context_menu,
MESH_MT_color_attribute_context_menu,
MESH_MT_attribute_context_menu,
MESH_UL_vgroups,
MESH_UL_fmaps,

View File

@ -60,6 +60,13 @@ struct CustomDataLayer *BKE_id_attribute_new(
struct ID *id, const char *name, int type, eAttrDomain domain, struct ReportList *reports);
bool BKE_id_attribute_remove(struct ID *id, const char *name, struct ReportList *reports);
/**
* Creates a duplicate attribute layer.
*/
struct CustomDataLayer *BKE_id_attribute_duplicate(struct ID *id,
struct CustomDataLayer *layer,
struct ReportList *reports);
struct CustomDataLayer *BKE_id_attribute_find(const struct ID *id,
const char *name,
int type,

View File

@ -208,6 +208,55 @@ CustomDataLayer *BKE_id_attribute_new(
return (index == -1) ? nullptr : &(customdata->layers[index]);
}
CustomDataLayer *BKE_id_attribute_duplicate(ID *id, CustomDataLayer *layer, ReportList *reports)
{
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
eCustomDataType type = (eCustomDataType)layer->type;
eAttrDomain domain = BKE_id_attribute_domain(id, layer);
CustomData *customdata = info[domain].customdata;
if (customdata == nullptr) {
BKE_report(reports, RPT_ERROR, "Attribute domain not supported by this geometry type");
return nullptr;
}
char name[MAX_CUSTOMDATA_LAYER_NAME];
char uniquename[MAX_CUSTOMDATA_LAYER_NAME];
/* Make a copy of name in case CustomData API reallocates the layers. */
BLI_strncpy(name, layer->name, MAX_CUSTOMDATA_LAYER_NAME);
BKE_id_attribute_calc_unique_name(id, layer->name, uniquename);
switch (GS(id->name)) {
case ID_ME: {
Mesh *me = (Mesh *)id;
BMEditMesh *em = me->edit_mesh;
if (em != nullptr) {
BM_data_layer_add_named(em->bm, customdata, type, uniquename);
}
else {
CustomData_add_layer_named(
customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename);
}
break;
}
default: {
CustomData_add_layer_named(
customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename);
break;
}
}
int from_index = CustomData_get_named_layer_index(customdata, type, name);
int to_index = CustomData_get_named_layer_index(customdata, type, uniquename);
CustomData_copy_data_layer(
customdata, customdata, from_index, to_index, 0, 0, info[domain].length);
return (to_index == -1) ? nullptr : &(customdata->layers[to_index]);
}
bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports)
{
if (BKE_id_attribute_required(id, name)) {
@ -283,7 +332,7 @@ CustomDataLayer *BKE_id_attribute_search(const ID *id,
}
CustomData *customdata = info[domain].customdata;
if (customdata == NULL) {
if (customdata == nullptr) {
continue;
}
@ -295,7 +344,7 @@ CustomDataLayer *BKE_id_attribute_search(const ID *id,
}
}
return NULL;
return nullptr;
}
int BKE_id_attributes_length(const ID *id, eAttrDomainMask domain_mask, eCustomDataMask mask)

View File

@ -498,6 +498,7 @@ static bool geometry_color_attributes_remove_poll(bContext *C)
return false;
}
void GEOMETRY_OT_color_attribute_remove(wmOperatorType *ot)
{
/* identifiers */
@ -513,6 +514,57 @@ void GEOMETRY_OT_color_attribute_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int geometry_color_attribute_duplicate_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
ID *id = static_cast<ID *>(ob->data);
CustomDataLayer *layer = BKE_id_attributes_active_color_get(id);
if (layer == nullptr) {
return OPERATOR_CANCELLED;
}
CustomDataLayer *newLayer = BKE_id_attribute_duplicate(id, layer, op->reports);
BKE_id_attributes_active_color_set(id, newLayer);
DEG_id_tag_update(id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, id);
return OPERATOR_FINISHED;
}
static bool geometry_color_attributes_duplicate_poll(bContext *C)
{
if (!geometry_attributes_poll(C)) {
return false;
}
Object *ob = ED_object_context(C);
ID *data = ob ? static_cast<ID *>(ob->data) : nullptr;
if (BKE_id_attributes_active_color_get(data) != nullptr) {
return true;
}
return false;
}
void GEOMETRY_OT_color_attribute_duplicate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Duplicate Color Attribute";
ot->description = "Duplicate color attribute";
ot->idname = "GEOMETRY_OT_color_attribute_duplicate";
/* api callbacks */
ot->exec = geometry_color_attribute_duplicate_exec;
ot->poll = geometry_color_attributes_duplicate_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static void geometry_attribute_convert_ui(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;

View File

@ -17,6 +17,7 @@ void GEOMETRY_OT_attribute_remove(struct wmOperatorType *ot);
void GEOMETRY_OT_color_attribute_add(struct wmOperatorType *ot);
void GEOMETRY_OT_color_attribute_remove(struct wmOperatorType *ot);
void GEOMETRY_OT_color_attribute_render_set(struct wmOperatorType *ot);
void GEOMETRY_OT_color_attribute_duplicate(struct wmOperatorType *ot);
void GEOMETRY_OT_attribute_convert(struct wmOperatorType *ot);
} // namespace blender::ed::geometry

View File

@ -22,5 +22,6 @@ void ED_operatortypes_geometry(void)
WM_operatortype_append(GEOMETRY_OT_color_attribute_add);
WM_operatortype_append(GEOMETRY_OT_color_attribute_remove);
WM_operatortype_append(GEOMETRY_OT_color_attribute_render_set);
WM_operatortype_append(GEOMETRY_OT_color_attribute_duplicate);
WM_operatortype_append(GEOMETRY_OT_attribute_convert);
}