Since "Applied Modifier" was always added last, it obscured more important messages when using the shortcut to delete modifiers. The purpose of the report when using the shortcut was to make it clear that something happened. Since another report does that anyway, only display the "Applied Modifier" report if the report list length hasn't changed.
942 lines
28 KiB
C
942 lines
28 KiB
C
/*
|
|
* 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 <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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 "UI_interface.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_modifier_get_info(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_findby_type(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;
|
|
}
|
|
|
|
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 false;
|
|
}
|
|
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
BLI_remlink(&ob->greasepencil_modifiers, md);
|
|
BKE_gpencil_modifier_free(md);
|
|
BKE_object_free_derived_caches(ob);
|
|
|
|
return true;
|
|
}
|
|
|
|
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 false;
|
|
}
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
DEG_relations_tag_update(bmain);
|
|
|
|
return true;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
bool 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 true;
|
|
}
|
|
|
|
bool 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 true;
|
|
}
|
|
|
|
bool ED_object_gpencil_modifier_move_to_index(ReportList *reports,
|
|
Object *ob,
|
|
GpencilModifierData *md,
|
|
const int index)
|
|
{
|
|
BLI_assert(md != NULL);
|
|
BLI_assert(index >= 0);
|
|
if (index >= BLI_listbase_count(&ob->greasepencil_modifiers)) {
|
|
BKE_report(reports, RPT_WARNING, "Cannot move modifier beyond the end of the stack");
|
|
return false;
|
|
}
|
|
|
|
int md_index = BLI_findindex(&ob->greasepencil_modifiers, md);
|
|
BLI_assert(md_index != -1);
|
|
if (md_index < index) {
|
|
/* Move modifier down in list. */
|
|
for (; md_index < index; md_index++) {
|
|
if (!ED_object_gpencil_modifier_move_down(reports, ob, md)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* Move modifier up in list. */
|
|
for (; md_index > index; md_index--) {
|
|
if (!ED_object_gpencil_modifier_move_up(reports, ob, md)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
|
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool gpencil_modifier_apply_obdata(
|
|
ReportList *reports, Main *bmain, Depsgraph *depsgraph, Object *ob, GpencilModifierData *md)
|
|
{
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
|
|
|
|
if (mti->isDisabled && mti->isDisabled(md, 0)) {
|
|
BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply");
|
|
return false;
|
|
}
|
|
|
|
if (ob->type == OB_GPENCIL) {
|
|
if (ELEM(NULL, ob, ob->data)) {
|
|
return false;
|
|
}
|
|
if (mti->bakeModifier == NULL) {
|
|
BKE_report(reports, RPT_ERROR, "Not implemented");
|
|
return false;
|
|
}
|
|
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 false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool 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 false;
|
|
}
|
|
|
|
if (((ID *)ob->data)->us > 1) {
|
|
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data");
|
|
return false;
|
|
}
|
|
}
|
|
else if (((ID *)ob->data)->us > 1) {
|
|
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data");
|
|
return false;
|
|
}
|
|
|
|
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 false;
|
|
}
|
|
|
|
BLI_remlink(&ob->greasepencil_modifiers, md);
|
|
BKE_gpencil_modifier_free(md);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ED_object_gpencil_modifier_copy(ReportList *reports, Object *ob, GpencilModifierData *md)
|
|
{
|
|
GpencilModifierData *nmd;
|
|
const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
|
|
GpencilModifierType type = md->type;
|
|
|
|
if (mti->flags & eGpencilModifierTypeFlag_Single) {
|
|
if (BKE_gpencil_modifiers_findby_type(ob, type)) {
|
|
BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
nmd->flag |= eGpencilModifierFlag_OverrideLibrary_Local;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ED_object_gpencil_modifier_copy_to_object(Object *ob_dst, GpencilModifierData *md)
|
|
{
|
|
BKE_object_copy_gpencil_modifier(ob_dst, md);
|
|
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob_dst);
|
|
DEG_id_tag_update(&ob_dst->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
|
|
}
|
|
|
|
/************************ 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_modifier_get_info(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 bool gpencil_edit_modifier_poll_generic(bContext *C,
|
|
StructRNA *rna_type,
|
|
int obtype_flag,
|
|
const bool is_liboverride_allowed)
|
|
{
|
|
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);
|
|
GpencilModifierData *mod = ptr.data; /* May be NULL. */
|
|
|
|
if (!ob || ID_IS_LINKED(ob)) {
|
|
return false;
|
|
}
|
|
if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) {
|
|
return false;
|
|
}
|
|
if (ptr.owner_id && ID_IS_LINKED(ptr.owner_id)) {
|
|
return false;
|
|
}
|
|
|
|
if (!is_liboverride_allowed && BKE_gpencil_modifier_is_nonlocal_in_liboverride(ob, mod)) {
|
|
CTX_wm_operator_poll_msg_set(
|
|
C, "Cannot edit modifiers coming from linked data in a library override");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool gpencil_edit_modifier_poll(bContext *C)
|
|
{
|
|
return gpencil_edit_modifier_poll_generic(C, &RNA_GpencilModifier, 0, false);
|
|
}
|
|
|
|
/* Used by operators performing actions allowed also on modifiers from the overridden linked object
|
|
* (not only from added 'local' ones). */
|
|
static bool gpencil_edit_modifier_liboverride_allowed_poll(bContext *C)
|
|
{
|
|
return gpencil_edit_modifier_poll_generic(C, &RNA_GpencilModifier, 0, true);
|
|
}
|
|
|
|
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 void gpencil_edit_modifier_report_property(wmOperatorType *ot)
|
|
{
|
|
PropertyRNA *prop = RNA_def_boolean(
|
|
ot->srna, "report", false, "Report", "Create a notification after the operation");
|
|
RNA_def_property_flag(prop, PROP_HIDDEN);
|
|
}
|
|
|
|
/**
|
|
* \param event: If this isn't NULL, the operator will also look for panels underneath
|
|
* the cursor with custom-data set to a modifier.
|
|
* \param r_retval: This should be used if #event is used in order to return
|
|
* #OPERATOR_PASS_THROUGH to check other operators with the same key set.
|
|
*/
|
|
static bool gpencil_edit_modifier_invoke_properties(bContext *C,
|
|
wmOperator *op,
|
|
const wmEvent *event,
|
|
int *r_retval)
|
|
{
|
|
if (RNA_struct_property_is_set(op->ptr, "modifier")) {
|
|
return true;
|
|
}
|
|
|
|
PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_GpencilModifier);
|
|
if (ctx_ptr.data != NULL) {
|
|
GpencilModifierData *md = ctx_ptr.data;
|
|
RNA_string_set(op->ptr, "modifier", md->name);
|
|
return true;
|
|
}
|
|
|
|
/* Check the custom data of panels under the mouse for a modifier. */
|
|
if (event != NULL) {
|
|
PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
|
|
|
|
if (!(panel_ptr == NULL || RNA_pointer_is_null(panel_ptr))) {
|
|
if (RNA_struct_is_a(panel_ptr->type, &RNA_GpencilModifier)) {
|
|
GpencilModifierData *md = panel_ptr->data;
|
|
RNA_string_set(op->ptr, "modifier", md->name);
|
|
return true;
|
|
}
|
|
|
|
BLI_assert(r_retval != NULL); /* We need the return value in this case. */
|
|
if (r_retval != NULL) {
|
|
*r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (r_retval != NULL) {
|
|
*r_retval = OPERATOR_CANCELLED;
|
|
}
|
|
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_findby_name(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 == NULL) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
/* Store name temporarily for report. */
|
|
char name[MAX_NAME];
|
|
strcpy(name, md->name);
|
|
|
|
if (!ED_object_gpencil_modifier_remove(op->reports, bmain, ob, md)) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
|
|
if (RNA_boolean_get(op->ptr, "report")) {
|
|
BKE_reportf(op->reports, RPT_INFO, "Removed modifier: %s", name);
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int gpencil_modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
int retval;
|
|
if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
|
|
return gpencil_modifier_remove_exec(C, op);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
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);
|
|
gpencil_edit_modifier_report_property(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 *event)
|
|
{
|
|
int retval;
|
|
if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
|
|
return gpencil_modifier_move_up_exec(C, op);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
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 *event)
|
|
{
|
|
int retval;
|
|
if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
|
|
return gpencil_modifier_move_down_exec(C, op);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/* ************************* Move to Index Gpencil Modifier Operator ************************* */
|
|
|
|
static int gpencil_modifier_move_to_index_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Object *ob = ED_object_active_context(C);
|
|
GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0);
|
|
int index = RNA_int_get(op->ptr, "index");
|
|
|
|
if (!ED_object_gpencil_modifier_move_to_index(op->reports, ob, md, index)) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int gpencil_modifier_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
int retval;
|
|
if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
|
|
return gpencil_modifier_move_to_index_exec(C, op);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
void OBJECT_OT_gpencil_modifier_move_to_index(wmOperatorType *ot)
|
|
{
|
|
ot->name = "Move Active Modifier to Index";
|
|
ot->idname = "OBJECT_OT_gpencil_modifier_move_to_index";
|
|
ot->description =
|
|
"Change the modifier's position in the list so it evaluates after the set number of "
|
|
"others";
|
|
|
|
ot->invoke = gpencil_modifier_move_to_index_invoke;
|
|
ot->exec = gpencil_modifier_move_to_index_exec;
|
|
ot->poll = gpencil_edit_modifier_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
edit_modifier_properties(ot);
|
|
RNA_def_int(
|
|
ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the modifier to", 0, INT_MAX);
|
|
}
|
|
|
|
/************************ 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");
|
|
const bool do_report = RNA_boolean_get(op->ptr, "report");
|
|
|
|
if (md == NULL) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
int reports_len;
|
|
char name[MAX_NAME];
|
|
if (do_report) {
|
|
reports_len = BLI_listbase_count(&op->reports->list);
|
|
strcpy(name, md->name); /* Store name temporarily since the modifier is removed. */
|
|
}
|
|
|
|
if (!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);
|
|
|
|
if (do_report) {
|
|
/* Only add this report if the operator didn't cause another one. The purpose here is
|
|
* to alert that something happened, and the previous report will do that anyway. */
|
|
if (BLI_listbase_count(&op->reports->list) == reports_len) {
|
|
BKE_reportf(op->reports, RPT_INFO, "Applied modifier: %s", name);
|
|
}
|
|
}
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int gpencil_modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
int retval;
|
|
if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
|
|
return gpencil_modifier_apply_exec(C, op);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
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);
|
|
gpencil_edit_modifier_report_property(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 *event)
|
|
{
|
|
int retval;
|
|
if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
|
|
return gpencil_modifier_copy_exec(C, op);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
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_liboverride_allowed_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
gpencil_edit_modifier_properties(ot);
|
|
}
|
|
|
|
/************************ Copy Modifier to Selected Operator *********************/
|
|
|
|
static int gpencil_modifier_copy_to_selected_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Object *obact = ED_object_active_context(C);
|
|
GpencilModifierData *md = gpencil_edit_modifier_property_get(op, obact, 0);
|
|
|
|
if (!md) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
if (obact->type != OB_GPENCIL) {
|
|
BKE_reportf(op->reports,
|
|
RPT_ERROR,
|
|
"Source object '%s' is not a grease pencil object",
|
|
obact->id.name + 2);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
|
|
if (ob == obact) {
|
|
continue;
|
|
}
|
|
|
|
if (ob->type != OB_GPENCIL) {
|
|
BKE_reportf(op->reports,
|
|
RPT_WARNING,
|
|
"Destination object '%s' is not a grease pencil object",
|
|
ob->id.name + 2);
|
|
continue;
|
|
}
|
|
|
|
/* This always returns true right now. */
|
|
BKE_object_copy_gpencil_modifier(ob, md);
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
|
|
}
|
|
CTX_DATA_END;
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static int gpencil_modifier_copy_to_selected_invoke(bContext *C,
|
|
wmOperator *op,
|
|
const wmEvent *event)
|
|
{
|
|
int retval;
|
|
if (gpencil_edit_modifier_invoke_properties(C, op, event, &retval)) {
|
|
return gpencil_modifier_copy_to_selected_exec(C, op);
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static bool gpencil_modifier_copy_to_selected_poll(bContext *C)
|
|
{
|
|
Object *obact = ED_object_active_context(C);
|
|
|
|
/* This could have a performance impact in the worst case, where there are many objects selected
|
|
* and none of them pass the check. But that should be uncommon, and this operator is only
|
|
* exposed in a drop-down menu anyway. */
|
|
bool found_supported_objects = false;
|
|
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
|
|
if (ob == obact) {
|
|
continue;
|
|
}
|
|
|
|
if (ob->type == OB_GPENCIL) {
|
|
found_supported_objects = true;
|
|
break;
|
|
}
|
|
}
|
|
CTX_DATA_END;
|
|
|
|
if (!found_supported_objects) {
|
|
CTX_wm_operator_poll_msg_set(C, "No supported objects were selected");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void OBJECT_OT_gpencil_modifier_copy_to_selected(wmOperatorType *ot)
|
|
{
|
|
ot->name = "Copy Modifier to Selected";
|
|
ot->description = "Copy the modifier from the active object to all selected objects";
|
|
ot->idname = "OBJECT_OT_gpencil_modifier_copy_to_selected";
|
|
|
|
ot->invoke = gpencil_modifier_copy_to_selected_invoke;
|
|
ot->exec = gpencil_modifier_copy_to_selected_exec;
|
|
ot->poll = gpencil_modifier_copy_to_selected_poll;
|
|
|
|
/* flags */
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
gpencil_edit_modifier_properties(ot);
|
|
}
|