/* * 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. * * The Original Code is Copyright (C) 2018 Blender Foundation. * All rights reserved. */ /** \file * \ingroup edobj */ #include #include #include #include "MEM_guardedalloc.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "BLI_listbase.h" #include "BLI_string_utf8.h" #include "BLI_utildefines.h" #include "BKE_context.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" #include "BKE_main.h" #include "BKE_object.h" #include "BKE_report.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" #include "DEG_depsgraph_query.h" #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" #include "ED_object.h" #include "ED_screen.h" #include "WM_api.h" #include "WM_types.h" #include "object_intern.h" /******************************** API ****************************/ GpencilModifierData *ED_object_gpencil_modifier_add( ReportList *reports, Main *bmain, Scene *UNUSED(scene), Object *ob, const char *name, int type) { GpencilModifierData *new_md = NULL; const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(type); if (ob->type != OB_GPENCIL) { BKE_reportf(reports, RPT_WARNING, "Modifiers cannot be added to object '%s'", ob->id.name + 2); return NULL; } if (mti->flags & eGpencilModifierTypeFlag_Single) { if (BKE_gpencil_modifiers_findByType(ob, type)) { BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed"); return NULL; } } /* get new modifier data to add */ new_md = BKE_gpencil_modifier_new(type); BLI_addtail(&ob->greasepencil_modifiers, new_md); if (name) { BLI_strncpy_utf8(new_md->name, name, sizeof(new_md->name)); } /* make sure modifier data has unique name */ BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, new_md); /* Enable edit mode visible by default. */ if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) { new_md->mode |= eGpencilModifierMode_Editmode; } bGPdata *gpd = ob->data; DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); DEG_relations_tag_update(bmain); return new_md; } /* Return true if the object has a modifier of type 'type' other than * the modifier pointed to be 'exclude', otherwise returns false. */ static bool UNUSED_FUNCTION(gpencil_object_has_modifier)(const Object *ob, const GpencilModifierData *exclude, GpencilModifierType type) { GpencilModifierData *md; for (md = ob->greasepencil_modifiers.first; md; md = md->next) { if ((md != exclude) && (md->type == type)) { return true; } } return false; } static bool gpencil_object_modifier_remove(Main *bmain, Object *ob, GpencilModifierData *md, bool *UNUSED(r_sort_depsgraph)) { /* It seems on rapid delete it is possible to * get called twice on same modifier, so make * sure it is in list. */ if (BLI_findindex(&ob->greasepencil_modifiers, md) == -1) { return 0; } DEG_relations_tag_update(bmain); BLI_remlink(&ob->greasepencil_modifiers, md); BKE_gpencil_modifier_free(md); BKE_object_free_derived_caches(ob); return 1; } bool ED_object_gpencil_modifier_remove(ReportList *reports, Main *bmain, Object *ob, GpencilModifierData *md) { bool sort_depsgraph = false; bool ok; ok = gpencil_object_modifier_remove(bmain, ob, md, &sort_depsgraph); if (!ok) { BKE_reportf(reports, RPT_ERROR, "Modifier '%s' not in object '%s'", md->name, ob->id.name); return 0; } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); DEG_relations_tag_update(bmain); return 1; } void ED_object_gpencil_modifier_clear(Main *bmain, Object *ob) { GpencilModifierData *md = ob->greasepencil_modifiers.first; bool sort_depsgraph = false; if (!md) { return; } while (md) { GpencilModifierData *next_md; next_md = md->next; gpencil_object_modifier_remove(bmain, ob, md, &sort_depsgraph); md = next_md; } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); DEG_relations_tag_update(bmain); } int ED_object_gpencil_modifier_move_up(ReportList *UNUSED(reports), Object *ob, GpencilModifierData *md) { if (md->prev) { BLI_remlink(&ob->greasepencil_modifiers, md); BLI_insertlinkbefore(&ob->greasepencil_modifiers, md->prev, md); } return 1; } int ED_object_gpencil_modifier_move_down(ReportList *UNUSED(reports), Object *ob, GpencilModifierData *md) { if (md->next) { BLI_remlink(&ob->greasepencil_modifiers, md); BLI_insertlinkafter(&ob->greasepencil_modifiers, md->next, md); } return 1; } static int gpencil_modifier_apply_obdata( ReportList *reports, Main *bmain, Depsgraph *depsgraph, Object *ob, GpencilModifierData *md) { const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); if (mti->isDisabled && mti->isDisabled(md, 0)) { BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply"); return 0; } if (ob->type == OB_GPENCIL) { if (ELEM(NULL, ob, ob->data)) { return 0; } else if (mti->bakeModifier == NULL) { BKE_report(reports, RPT_ERROR, "Not implemented"); return 0; } mti->bakeModifier(bmain, depsgraph, md, ob); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } else { BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type"); return 0; } return 1; } int ED_object_gpencil_modifier_apply(Main *bmain, ReportList *reports, Depsgraph *depsgraph, Object *ob, GpencilModifierData *md, int UNUSED(mode)) { if (ob->type == OB_GPENCIL) { if (ob->mode != OB_MODE_OBJECT) { BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in paint, sculpt or edit mode"); return 0; } if (((ID *)ob->data)->us > 1) { BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data"); return 0; } } else if (((ID *)ob->data)->us > 1) { BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data"); return 0; } if (md != ob->greasepencil_modifiers.first) { BKE_report(reports, RPT_INFO, "Applied modifier was not first, result may not be as expected"); } if (!gpencil_modifier_apply_obdata(reports, bmain, depsgraph, ob, md)) { return 0; } BLI_remlink(&ob->greasepencil_modifiers, md); BKE_gpencil_modifier_free(md); return 1; } int ED_object_gpencil_modifier_copy(ReportList *reports, Object *ob, GpencilModifierData *md) { GpencilModifierData *nmd; const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); GpencilModifierType type = md->type; if (mti->flags & eGpencilModifierTypeFlag_Single) { if (BKE_gpencil_modifiers_findByType(ob, type)) { BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed"); return 0; } } nmd = BKE_gpencil_modifier_new(md->type); BKE_gpencil_modifier_copyData(md, nmd); BLI_insertlinkafter(&ob->greasepencil_modifiers, md, nmd); BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, nmd); return 1; } /************************ add modifier operator *********************/ static int gpencil_modifier_add_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); Object *ob = ED_object_active_context(C); int type = RNA_enum_get(op->ptr, "type"); if (!ED_object_gpencil_modifier_add(op->reports, bmain, scene, ob, NULL, type)) { return OPERATOR_CANCELLED; } WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); return OPERATOR_FINISHED; } static const EnumPropertyItem *gpencil_modifier_add_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { Object *ob = ED_object_active_context(C); EnumPropertyItem *item = NULL; const EnumPropertyItem *md_item, *group_item = NULL; const GpencilModifierTypeInfo *mti; int totitem = 0, a; if (!ob) { return rna_enum_object_greasepencil_modifier_type_items; } for (a = 0; rna_enum_object_greasepencil_modifier_type_items[a].identifier; a++) { md_item = &rna_enum_object_greasepencil_modifier_type_items[a]; if (md_item->identifier[0]) { mti = BKE_gpencil_modifierType_getInfo(md_item->value); if (mti->flags & eGpencilModifierTypeFlag_NoUserAdd) { continue; } } else { group_item = md_item; md_item = NULL; continue; } if (group_item) { RNA_enum_item_add(&item, &totitem, group_item); group_item = NULL; } RNA_enum_item_add(&item, &totitem, md_item); } RNA_enum_item_end(&item, &totitem); *r_free = true; return item; } void OBJECT_OT_gpencil_modifier_add(wmOperatorType *ot) { PropertyRNA *prop; /* identifiers */ ot->name = "Add Modifier"; ot->description = "Add a procedural operation/effect to the active grease pencil object"; ot->idname = "OBJECT_OT_gpencil_modifier_add"; /* api callbacks */ ot->invoke = WM_menu_invoke; ot->exec = gpencil_modifier_add_exec; ot->poll = ED_operator_object_active_editable; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ prop = RNA_def_enum(ot->srna, "type", rna_enum_object_modifier_type_items, eGpencilModifierType_Thick, "Type", ""); RNA_def_enum_funcs(prop, gpencil_modifier_add_itemf); ot->prop = prop; } /********** generic functions for operators using mod names and data context *********************/ static int gpencil_edit_modifier_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag) { PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type); Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); if (!ob || ID_IS_LINKED(ob)) { return 0; } if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) { return 0; } if (ptr.owner_id && ID_IS_LINKED(ptr.owner_id)) { return 0; } if (ID_IS_OVERRIDE_LIBRARY(ob)) { CTX_wm_operator_poll_msg_set(C, "Cannot edit modifiers coming from library override"); return (((GpencilModifierData *)ptr.data)->flag & eGpencilModifierFlag_OverrideLibrary_Local) != 0; } return 1; } static bool gpencil_edit_modifier_poll(bContext *C) { return gpencil_edit_modifier_poll_generic(C, &RNA_GpencilModifier, 0); } static void gpencil_edit_modifier_properties(wmOperatorType *ot) { PropertyRNA *prop = RNA_def_string( ot->srna, "modifier", NULL, MAX_NAME, "Modifier", "Name of the modifier to edit"); RNA_def_property_flag(prop, PROP_HIDDEN); } static int gpencil_edit_modifier_invoke_properties(bContext *C, wmOperator *op) { GpencilModifierData *md; if (RNA_struct_property_is_set(op->ptr, "modifier")) { return true; } else { PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_GpencilModifier); if (ptr.data) { md = ptr.data; RNA_string_set(op->ptr, "modifier", md->name); return true; } } return false; } static GpencilModifierData *gpencil_edit_modifier_property_get(wmOperator *op, Object *ob, int type) { char modifier_name[MAX_NAME]; GpencilModifierData *md; RNA_string_get(op->ptr, "modifier", modifier_name); md = BKE_gpencil_modifiers_findByName(ob, modifier_name); if (md && type != 0 && md->type != type) { md = NULL; } return md; } /************************ remove modifier operator *********************/ static int gpencil_modifier_remove_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Object *ob = ED_object_active_context(C); GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); if (!md || !ED_object_gpencil_modifier_remove(op->reports, bmain, ob, md)) { return OPERATOR_CANCELLED; } WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); return OPERATOR_FINISHED; } static int gpencil_modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { if (gpencil_edit_modifier_invoke_properties(C, op)) { return gpencil_modifier_remove_exec(C, op); } else { return OPERATOR_CANCELLED; } } void OBJECT_OT_gpencil_modifier_remove(wmOperatorType *ot) { ot->name = "Remove Grease Pencil Modifier"; ot->description = "Remove a modifier from the active grease pencil object"; ot->idname = "OBJECT_OT_gpencil_modifier_remove"; ot->invoke = gpencil_modifier_remove_invoke; ot->exec = gpencil_modifier_remove_exec; ot->poll = gpencil_edit_modifier_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; gpencil_edit_modifier_properties(ot); } /************************ move up modifier operator *********************/ static int gpencil_modifier_move_up_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_active_context(C); GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); if (!md || !ED_object_gpencil_modifier_move_up(op->reports, ob, md)) { return OPERATOR_CANCELLED; } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); return OPERATOR_FINISHED; } static int gpencil_modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { if (gpencil_edit_modifier_invoke_properties(C, op)) { return gpencil_modifier_move_up_exec(C, op); } else { return OPERATOR_CANCELLED; } } void OBJECT_OT_gpencil_modifier_move_up(wmOperatorType *ot) { ot->name = "Move Up Modifier"; ot->description = "Move modifier up in the stack"; ot->idname = "OBJECT_OT_gpencil_modifier_move_up"; ot->invoke = gpencil_modifier_move_up_invoke; ot->exec = gpencil_modifier_move_up_exec; ot->poll = gpencil_edit_modifier_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; gpencil_edit_modifier_properties(ot); } /************************ move down modifier operator *********************/ static int gpencil_modifier_move_down_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_active_context(C); GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); if (!md || !ED_object_gpencil_modifier_move_down(op->reports, ob, md)) { return OPERATOR_CANCELLED; } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); return OPERATOR_FINISHED; } static int gpencil_modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { if (gpencil_edit_modifier_invoke_properties(C, op)) { return gpencil_modifier_move_down_exec(C, op); } else { return OPERATOR_CANCELLED; } } void OBJECT_OT_gpencil_modifier_move_down(wmOperatorType *ot) { ot->name = "Move Down Modifier"; ot->description = "Move modifier down in the stack"; ot->idname = "OBJECT_OT_gpencil_modifier_move_down"; ot->invoke = gpencil_modifier_move_down_invoke; ot->exec = gpencil_modifier_move_down_exec; ot->poll = gpencil_edit_modifier_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; gpencil_edit_modifier_properties(ot); } /************************ apply modifier operator *********************/ static int gpencil_modifier_apply_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Object *ob = ED_object_active_context(C); GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); int apply_as = RNA_enum_get(op->ptr, "apply_as"); if (!md || !ED_object_gpencil_modifier_apply(bmain, op->reports, depsgraph, ob, md, apply_as)) { return OPERATOR_CANCELLED; } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); return OPERATOR_FINISHED; } static int gpencil_modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { if (gpencil_edit_modifier_invoke_properties(C, op)) { return gpencil_modifier_apply_exec(C, op); } else { return OPERATOR_CANCELLED; } } static const EnumPropertyItem gpencil_modifier_apply_as_items[] = { {MODIFIER_APPLY_DATA, "DATA", 0, "Object Data", "Apply modifier to the object's data"}, {MODIFIER_APPLY_SHAPE, "SHAPE", 0, "New Shape", "Apply deform-only modifier to a new shape on this object"}, {0, NULL, 0, NULL, NULL}, }; void OBJECT_OT_gpencil_modifier_apply(wmOperatorType *ot) { ot->name = "Apply Modifier"; ot->description = "Apply modifier and remove from the stack"; ot->idname = "OBJECT_OT_gpencil_modifier_apply"; ot->invoke = gpencil_modifier_apply_invoke; ot->exec = gpencil_modifier_apply_exec; ot->poll = gpencil_edit_modifier_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; RNA_def_enum(ot->srna, "apply_as", gpencil_modifier_apply_as_items, MODIFIER_APPLY_DATA, "Apply as", "How to apply the modifier to the geometry"); gpencil_edit_modifier_properties(ot); } /************************ copy modifier operator *********************/ static int gpencil_modifier_copy_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_active_context(C); GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); if (!md || !ED_object_gpencil_modifier_copy(op->reports, ob, md)) { return OPERATOR_CANCELLED; } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); return OPERATOR_FINISHED; } static int gpencil_modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { if (gpencil_edit_modifier_invoke_properties(C, op)) { return gpencil_modifier_copy_exec(C, op); } else { return OPERATOR_CANCELLED; } } void OBJECT_OT_gpencil_modifier_copy(wmOperatorType *ot) { ot->name = "Copy Modifier"; ot->description = "Duplicate modifier at the same position in the stack"; ot->idname = "OBJECT_OT_gpencil_modifier_copy"; ot->invoke = gpencil_modifier_copy_invoke; ot->exec = gpencil_modifier_copy_exec; ot->poll = gpencil_edit_modifier_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; gpencil_edit_modifier_properties(ot); }