diff options
-rw-r--r-- | source/blender/blenkernel/BKE_object.h | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object.c | 10 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/particle.c | 2 | ||||
-rw-r--r-- | source/blender/editors/object/object_gpencil_modifier.c | 96 | ||||
-rw-r--r-- | source/blender/editors/object/object_intern.h | 2 | ||||
-rw-r--r-- | source/blender/editors/object/object_modifier.c | 225 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 2 | ||||
-rw-r--r-- | source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c | 5 | ||||
-rw-r--r-- | source/blender/modifiers/intern/MOD_ui_common.c | 5 |
9 files changed, 348 insertions, 0 deletions
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index a620d9af946..cd253ca4bbc 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -75,6 +75,7 @@ bool BKE_object_modifier_gpencil_use_time(struct Object *ob, struct GpencilModif bool BKE_object_shaderfx_use_time(struct Object *ob, struct ShaderFxData *md); +bool BKE_object_supports_modifiers(const struct Object *ob); bool BKE_object_support_modifier_type_check(const struct Object *ob, int modifier_type); /* Active modifier. */ diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 7ed906e2cea..163e7d51235 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1315,6 +1315,15 @@ ModifierData *BKE_object_active_modifier(const Object *ob) return NULL; } +/** + * \return True if the object's type supports regular modifiers (not grease pencil modifiers). + */ +bool BKE_object_supports_modifiers(const Object *ob) +{ + return ( + ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE, OB_POINTCLOUD, OB_VOLUME)); +} + bool BKE_object_support_modifier_type_check(const Object *ob, int modifier_type) { const ModifierTypeInfo *mti = BKE_modifier_get_info(modifier_type); @@ -1377,6 +1386,7 @@ bool BKE_object_copy_modifier(struct Object *ob_dst, const struct Object *ob_src BKE_modifier_copydata(md, nmd); BLI_addtail(&ob_dst->modifiers, nmd); BKE_modifier_unique_name(&ob_dst->modifiers, nmd); + BKE_object_modifier_set_active(ob_dst, nmd); return true; } diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index dce45f44583..335913c9b8e 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -75,6 +75,7 @@ #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_modifier.h" +#include "BKE_object.h" #include "BKE_particle.h" #include "BKE_pointcache.h" #include "BKE_scene.h" @@ -3948,6 +3949,7 @@ static ModifierData *object_add_or_copy_particle_system( psmd = (ParticleSystemModifierData *)md; psmd->psys = psys; BLI_addtail(&ob->modifiers, md); + BKE_object_modifier_set_active(ob, md); psys->totpart = 0; psys->flag = PSYS_CURRENT; diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c index e5feb74df26..eb3ccf52c6f 100644 --- a/source/blender/editors/object/object_gpencil_modifier.c +++ b/source/blender/editors/object/object_gpencil_modifier.c @@ -835,3 +835,99 @@ void OBJECT_OT_gpencil_modifier_copy(wmOperatorType *ot) 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); +} diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 89ade5cc49d..00426e96a81 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -175,6 +175,7 @@ void OBJECT_OT_modifier_apply(struct wmOperatorType *ot); void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot); void OBJECT_OT_modifier_convert(struct wmOperatorType *ot); void OBJECT_OT_modifier_copy(struct wmOperatorType *ot); +void OBJECT_OT_modifier_copy_to_selected(struct wmOperatorType *ot); void OBJECT_OT_modifier_set_active(struct wmOperatorType *ot); void OBJECT_OT_multires_subdivide(struct wmOperatorType *ot); void OBJECT_OT_multires_reshape(struct wmOperatorType *ot); @@ -203,6 +204,7 @@ void OBJECT_OT_gpencil_modifier_move_down(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_move_to_index(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_apply(struct wmOperatorType *ot); void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_copy_to_selected(struct wmOperatorType *ot); /* object_shader_fx.c */ void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 3111003703f..e9c84353b80 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -30,6 +30,8 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_curve_types.h" +#include "DNA_dynamicpaint_types.h" +#include "DNA_fluid_types.h" #include "DNA_key_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" @@ -1682,6 +1684,229 @@ void OBJECT_OT_modifier_set_active(wmOperatorType *ot) } /** \} */ +/** \name Copy Modifier To Selected Operator + * \{ */ + +/* If the modifier uses particles, copy particle system to destination object + * or reuse existing if it has the same ParticleSettings */ +static void copy_or_reuse_particle_system(bContext *C, Object *ob, ModifierData *md) +{ + ParticleSystem *psys_on_modifier = NULL; + + if (md->type == eModifierType_DynamicPaint) { + DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; + if (pmd->brush && pmd->brush->psys) { + psys_on_modifier = pmd->brush->psys; + } + } + else if (md->type == eModifierType_Fluid) { + FluidModifierData *fmd = (FluidModifierData *)md; + if (fmd->type == MOD_FLUID_TYPE_FLOW) { + if (fmd->flow && fmd->flow->psys) { + psys_on_modifier = fmd->flow->psys; + } + } + } + + if (!psys_on_modifier) { + return; + } + + ParticleSystem *psys_on_new_modifier = NULL; + + /* Check if a particle system with the same particle settings + * already exists on the destination object. */ + LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) { + if (psys_on_modifier->part == psys->part) { + psys_on_new_modifier = psys; + break; + } + } + + /* If it does not exist, copy the particle system to the destination object. */ + if (!psys_on_new_modifier) { + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + object_copy_particle_system(bmain, scene, ob, psys_on_modifier); + + LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) { + if (psys_on_modifier->part == psys->part) { + psys_on_new_modifier = psys; + } + } + } + + /* Update the modifier to point to the new/existing particle system. */ + LISTBASE_FOREACH (ModifierData *, new_md, &ob->modifiers) { + if (new_md->type == eModifierType_DynamicPaint) { + DynamicPaintModifierData *new_pmd = (DynamicPaintModifierData *)new_md; + + if (psys_on_modifier == new_pmd->brush->psys) { + new_pmd->brush->psys = psys_on_new_modifier; + } + } + else if (new_md->type == eModifierType_Fluid) { + FluidModifierData *new_fmd = (FluidModifierData *)new_md; + + if (psys_on_modifier == new_fmd->flow->psys) { + new_fmd->flow->psys = psys_on_new_modifier; + } + } + } +} + +static int modifier_copy_to_selected_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *obact = ED_object_active_context(C); + ModifierData *md = edit_modifier_property_get(op, obact, 0); + + if (!md) { + return OPERATOR_CANCELLED; + } + + int num_copied = 0; + const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); + + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if (ob == obact) { + continue; + } + + /* Checked in #BKE_object_copy_modifier, but check here too so we can give a better message. */ + if (!BKE_object_support_modifier_type_check(ob, md->type)) { + BKE_reportf(op->reports, + RPT_WARNING, + "Object '%s' does not support %s modifiers", + ob->id.name + 2, + mti->name); + continue; + } + + if (mti->flags & eModifierTypeFlag_Single) { + if (BKE_modifiers_findby_type(ob, md->type)) { + BKE_reportf(op->reports, + RPT_WARNING, + "Modifier can only be added once to object '%s'", + ob->id.name + 2); + continue; + } + } + + if (md->type == eModifierType_ParticleSystem) { + ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md; + object_copy_particle_system(bmain, scene, ob, psmd->psys); + } + else { + if (!BKE_object_copy_modifier(ob, obact, md)) { + BKE_reportf(op->reports, + RPT_ERROR, + "Copying modifier '%s' to object '%s' failed", + md->name, + ob->id.name + 2); + } + } + + if (ELEM(md->type, eModifierType_DynamicPaint, eModifierType_Fluid)) { + copy_or_reuse_particle_system(C, ob, md); + } + + num_copied++; + 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; + + if (num_copied > 0) { + DEG_relations_tag_update(bmain); + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Modifier '%s' was not copied to any objects", md->name); + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +static int modifier_copy_to_selected_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + if (edit_modifier_invoke_properties(C, op)) { + return modifier_copy_to_selected_exec(C, op); + } + /* Work around multiple operators using the same shortcut. */ + return (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED); +} + +static bool modifier_copy_to_selected_poll(bContext *C) +{ + PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier); + Object *obact = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); + ModifierData *md = ptr.data; + + /* This just mirrors the check in #BKE_object_copy_modifier, + * but there is no reasoning for it there. */ + if (md && ELEM(md->type, eModifierType_Hook, eModifierType_Collision)) { + CTX_wm_operator_poll_msg_set(C, "Not supported for \"Collision\" or \"Hook\" modifiers"); + return false; + } + + if (!obact) { + CTX_wm_operator_poll_msg_set(C, "No selected object is active"); + return false; + } + + if (!BKE_object_supports_modifiers(obact)) { + CTX_wm_operator_poll_msg_set(C, "Object type of source object is not supported"); + return false; + } + + /* This could have a performance impact in the worst case, where there are many objects selected + * and none of them pass either of the checks. 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 (!md && BKE_object_supports_modifiers(ob)) { + /* Skip type check if modifier could not be found ("modifier" context variable not set). */ + found_supported_objects = true; + break; + } + else if (BKE_object_support_modifier_type_check(ob, md->type)) { + 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_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_modifier_copy_to_selected"; + + ot->invoke = modifier_copy_to_selected_invoke; + ot->exec = modifier_copy_to_selected_exec; + ot->poll = modifier_copy_to_selected_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + +/** \} */ /* ------------------------------------------------------------------- */ /** \name Multires Delete Higher Levels Operator diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 2e5a75ffa7d..964d9898ac0 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -137,6 +137,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_modifier_apply_as_shapekey); WM_operatortype_append(OBJECT_OT_modifier_convert); WM_operatortype_append(OBJECT_OT_modifier_copy); + WM_operatortype_append(OBJECT_OT_modifier_copy_to_selected); WM_operatortype_append(OBJECT_OT_modifier_set_active); WM_operatortype_append(OBJECT_OT_multires_subdivide); WM_operatortype_append(OBJECT_OT_multires_reshape); @@ -159,6 +160,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_gpencil_modifier_move_to_index); WM_operatortype_append(OBJECT_OT_gpencil_modifier_apply); WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy_to_selected); /* shader fx */ WM_operatortype_append(OBJECT_OT_shaderfx_add); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c index d3ba5bf8b37..05e7a23bc82 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c @@ -270,6 +270,11 @@ static void gpencil_modifier_ops_extra_draw(bContext *C, uiLayout *layout, void ICON_DUPLICATE, "OBJECT_OT_gpencil_modifier_copy"); + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy to Selected"), + 0, + "OBJECT_OT_gpencil_modifier_copy_to_selected"); + uiItemS(layout); /* Move to first. */ diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 55dbfdf478f..58eeba35ca9 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -256,6 +256,11 @@ static void modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v) "OBJECT_OT_modifier_copy"); } + uiItemO(layout, + CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy to Selected"), + 0, + "OBJECT_OT_modifier_copy_to_selected"); + uiItemS(layout); /* Move to first. */ |