diff options
-rw-r--r-- | source/blender/blenkernel/intern/gpencil_modifier.c | 12 | ||||
-rw-r--r-- | source/blender/editors/object/object_gpencil_modifier.c | 236 | ||||
-rw-r--r-- | source/blender/editors/object/object_intern.h | 4 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 4 | ||||
-rw-r--r-- | source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c | 211 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_gpencil_modifier_defaults.h | 12 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_gpencil_modifier_types.h | 23 | ||||
-rw-r--r-- | source/blender/makesdna/intern/dna_defaults.c | 2 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_gpencil_modifier.c | 123 |
9 files changed, 616 insertions, 11 deletions
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 70b8717abbe..191231f2b43 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -904,6 +904,11 @@ void BKE_gpencil_modifier_blend_write(BlendWriter *writer, ListBase *modbase) BLO_write_struct_array( writer, DashGpencilModifierSegment, gpmd->segments_len, gpmd->segments); } + else if (md->type == eGpencilModifierType_Time) { + TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)md; + BLO_write_struct_array( + writer, TimeGpencilModifierSegment, gpmd->segments_len, gpmd->segments); + } } } @@ -990,6 +995,13 @@ void BKE_gpencil_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb) gpmd->segments[i].dmd = gpmd; } } + else if (md->type == eGpencilModifierType_Time) { + TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)md; + BLO_read_data_address(reader, &gpmd->segments); + for (int i = 0; i < gpmd->segments_len; i++) { + gpmd->segments[i].gpmd = gpmd; + } + } if (md->type == eGpencilModifierType_Shrinkwrap) { ShrinkwrapGpencilModifierData *gpmd = (ShrinkwrapGpencilModifierData *)md; gpmd->cache_data = NULL; diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c index 42ac6d166b4..e85947534cd 100644 --- a/source/blender/editors/object/object_gpencil_modifier.c +++ b/source/blender/editors/object/object_gpencil_modifier.c @@ -934,6 +934,242 @@ void OBJECT_OT_gpencil_modifier_copy_to_selected(wmOperatorType *ot) gpencil_edit_modifier_properties(ot); } +/************************* Time Offset Advanced Modifier *******************************/ + +static bool time_segment_poll(bContext *C) +{ + return gpencil_edit_modifier_poll_generic(C, &RNA_TimeGpencilModifier, 0, false); +} + +static bool time_segment_name_exists_fn(void *arg, const char *name) +{ + const TimeGpencilModifierData *gpmd = (const TimeGpencilModifierData *)arg; + for (int i = 0; i < gpmd->segments_len; i++) { + if (STREQ(gpmd->segments[i].name, name) && gpmd->segments[i].name != name) { + return true; + } + } + return false; +} + +static int time_segment_add_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)gpencil_edit_modifier_property_get( + op, ob, eGpencilModifierType_Time); + if (gpmd == NULL) { + return OPERATOR_CANCELLED; + } + const int new_active_index = gpmd->segment_active_index + 1; + TimeGpencilModifierSegment *new_segments = MEM_malloc_arrayN( + gpmd->segments_len + 1, sizeof(TimeGpencilModifierSegment), __func__); + + if (gpmd->segments_len != 0) { + /* Copy the segments before the new segment. */ + memcpy(new_segments, gpmd->segments, sizeof(TimeGpencilModifierSegment) * new_active_index); + /* Copy the segments after the new segment. */ + memcpy(new_segments + new_active_index + 1, + gpmd->segments + new_active_index, + sizeof(TimeGpencilModifierSegment) * (gpmd->segments_len - new_active_index)); + } + + /* Create the new segment. */ + TimeGpencilModifierSegment *ds = &new_segments[new_active_index]; + memcpy( + ds, DNA_struct_default_get(TimeGpencilModifierSegment), sizeof(TimeGpencilModifierSegment)); + BLI_uniquename_cb( + time_segment_name_exists_fn, gpmd, DATA_("Segment"), '.', ds->name, sizeof(ds->name)); + ds->gpmd = gpmd; + + MEM_SAFE_FREE(gpmd->segments); + gpmd->segments = new_segments; + gpmd->segments_len++; + gpmd->segment_active_index++; + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int time_segment_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op, NULL, NULL)) { + return time_segment_add_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void GPENCIL_OT_time_segment_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Segment"; + ot->description = "Add a segment to the time modifier"; + ot->idname = "GPENCIL_OT_time_segment_add"; + + /* api callbacks */ + ot->poll = time_segment_poll; + ot->invoke = time_segment_add_invoke; + ot->exec = time_segment_add_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} + +static int time_segment_remove_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + + TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)gpencil_edit_modifier_property_get( + op, ob, eGpencilModifierType_Time); + + if (gpmd->segment_active_index < 0 || gpmd->segment_active_index >= gpmd->segments_len) { + return OPERATOR_CANCELLED; + } + + if (gpmd->segments_len == 1) { + MEM_SAFE_FREE(gpmd->segments); + gpmd->segment_active_index = -1; + } + else { + TimeGpencilModifierSegment *new_segments = MEM_malloc_arrayN( + gpmd->segments_len, sizeof(TimeGpencilModifierSegment), __func__); + + /* Copy the segments before the deleted segment. */ + memcpy(new_segments, + gpmd->segments, + sizeof(TimeGpencilModifierSegment) * gpmd->segment_active_index); + + /* Copy the segments after the deleted segment. */ + memcpy(new_segments + gpmd->segment_active_index, + gpmd->segments + gpmd->segment_active_index + 1, + sizeof(TimeGpencilModifierSegment) * + (gpmd->segments_len - gpmd->segment_active_index - 1)); + + MEM_freeN(gpmd->segments); + gpmd->segments = new_segments; + gpmd->segment_active_index = MAX2(gpmd->segment_active_index - 1, 0); + } + + gpmd->segments_len--; + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int time_segment_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op, NULL, NULL)) { + return time_segment_remove_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void GPENCIL_OT_time_segment_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Time Segment"; + ot->description = "Remove the active segment from the time modifier"; + ot->idname = "GPENCIL_OT_time_segment_remove"; + + /* api callbacks */ + ot->poll = time_segment_poll; + ot->invoke = time_segment_remove_invoke; + ot->exec = time_segment_remove_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); + + RNA_def_int( + ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of the segment to remove", 0, INT_MAX); +} + +enum { + GP_TIME_SEGEMENT_MOVE_UP = -1, + GP_TIME_SEGEMENT_MOVE_DOWN = 1, +}; + +static int time_segment_move_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + + TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)gpencil_edit_modifier_property_get( + op, ob, eGpencilModifierType_Time); + + if (gpmd->segments_len < 2) { + return OPERATOR_CANCELLED; + } + + const int direction = RNA_enum_get(op->ptr, "type"); + if (direction == GP_TIME_SEGEMENT_MOVE_UP) { + if (gpmd->segment_active_index == 0) { + return OPERATOR_CANCELLED; + } + + SWAP(TimeGpencilModifierSegment, + gpmd->segments[gpmd->segment_active_index], + gpmd->segments[gpmd->segment_active_index - 1]); + + gpmd->segment_active_index--; + } + else if (direction == GP_TIME_SEGEMENT_MOVE_DOWN) { + if (gpmd->segment_active_index == gpmd->segments_len - 1) { + return OPERATOR_CANCELLED; + } + + SWAP(TimeGpencilModifierSegment, + gpmd->segments[gpmd->segment_active_index], + gpmd->segments[gpmd->segment_active_index + 1]); + + gpmd->segment_active_index++; + } + else { + return OPERATOR_CANCELLED; + } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int time_segment_move_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op, NULL, NULL)) { + return time_segment_move_exec(C, op); + } + return OPERATOR_CANCELLED; +} + +void GPENCIL_OT_time_segment_move(wmOperatorType *ot) +{ + static const EnumPropertyItem segment_move[] = { + {GP_TIME_SEGEMENT_MOVE_UP, "UP", 0, "Up", ""}, + {GP_TIME_SEGEMENT_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + /* identifiers */ + ot->name = "Move Time Segment"; + ot->description = "Move the active time segment up or down"; + ot->idname = "GPENCIL_OT_time_segment_move"; + + /* api callbacks */ + ot->poll = time_segment_poll; + ot->invoke = time_segment_move_invoke; + ot->exec = time_segment_move_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); + + ot->prop = RNA_def_enum(ot->srna, "type", segment_move, 0, "Type", ""); +} + /************************* Dash Modifier *******************************/ static bool dash_segment_poll(bContext *C) diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 63f010cd526..b136f311557 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -221,6 +221,10 @@ void GPENCIL_OT_segment_add(struct wmOperatorType *ot); void GPENCIL_OT_segment_remove(struct wmOperatorType *ot); void GPENCIL_OT_segment_move(struct wmOperatorType *ot); +void GPENCIL_OT_time_segment_add(struct wmOperatorType *ot); +void GPENCIL_OT_time_segment_remove(struct wmOperatorType *ot); +void GPENCIL_OT_time_segment_move(struct wmOperatorType *ot); + /* object_shader_fx.c */ void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 24a4556b075..a73cc98c78e 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -151,6 +151,10 @@ void ED_operatortypes_object(void) WM_operatortype_append(GPENCIL_OT_segment_remove); WM_operatortype_append(GPENCIL_OT_segment_move); + WM_operatortype_append(GPENCIL_OT_time_segment_add); + WM_operatortype_append(GPENCIL_OT_time_segment_remove); + WM_operatortype_append(GPENCIL_OT_time_segment_move); + /* grease pencil line art */ WM_operatortypes_lineart(); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c index 1d45030b68b..e3d49ba37d5 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c @@ -9,6 +9,11 @@ #include <stdlib.h> #include <string.h> +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -16,20 +21,33 @@ #include "DNA_defaults.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" #include "BKE_gpencil_modifier.h" +#include "BKE_lib_query.h" +#include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_screen.h" -#include "UI_interface.h" -#include "UI_resources.h" - #include "RNA_access.h" +#include "RNA_prototypes.h" #include "MOD_gpencil_modifiertypes.h" #include "MOD_gpencil_ui_common.h" +#include "MOD_gpencil_util.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "WM_api.h" + +#include "DEG_depsgraph.h" static void initData(GpencilModifierData *md) { @@ -38,11 +56,26 @@ static void initData(GpencilModifierData *md) BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier)); MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(TimeGpencilModifierData), modifier); + TimeGpencilModifierSegment *ds = DNA_struct_default_alloc(TimeGpencilModifierSegment); + ds->gpmd = gpmd; + BLI_strncpy(ds->name, DATA_("Segment"), sizeof(ds->name)); + + gpmd->segments = ds; } static void copyData(const GpencilModifierData *md, GpencilModifierData *target) { + TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)target; + const TimeGpencilModifierData *gpmd_src = (const TimeGpencilModifierData *)md; BKE_gpencil_modifier_copydata_generic(md, target); + gpmd->segments = MEM_dupallocN(gpmd_src->segments); +} + +static void freeData(GpencilModifierData *md) +{ + TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)md; + + MEM_SAFE_FREE(gpmd->segments); } static int remapTime(struct GpencilModifierData *md, @@ -60,6 +93,7 @@ static int remapTime(struct GpencilModifierData *md, int efra = custom ? mmd->efra : scene->r.efra; int offset = mmd->offset; int nfra = 0; + CLAMP_MIN(sfra, 0); CLAMP_MIN(efra, 0); @@ -100,6 +134,7 @@ static int remapTime(struct GpencilModifierData *md, /* apply frame scale */ cfra *= mmd->frame_scale; + CLAMP_MIN(cfra, 1); /* if fix mode, return predefined frame number */ if (mmd->mode == GP_TIME_MODE_FIX) { @@ -146,10 +181,111 @@ static int remapTime(struct GpencilModifierData *md, } } + if (mmd->mode == GP_TIME_MODE_CHAIN) { + int sequence_length = 0; + int frame_key = 0; + int *segment_arr; + int start, end; + if (mmd->segments_len > 0) { + for (int i = 0; i < mmd->segments_len; i++) { + start = mmd->segments[i].seg_start; + end = mmd->segments[i].seg_end; + if (mmd->segments[i].seg_end < mmd->segments[i].seg_start) { + start = mmd->segments[i].seg_end; + end = mmd->segments[i].seg_start; + } + + if (ELEM(mmd->segments[i].seg_mode, GP_TIME_SEG_MODE_PINGPONG)) { + sequence_length += ((end - start) * mmd->segments[i].seg_repeat) * 2 + 1; + } + else { + sequence_length += (((end - start + 1)) * mmd->segments[i].seg_repeat); + } + } + segment_arr = MEM_malloc_arrayN(sequence_length, sizeof(int *), __func__); + + for (int i = 0; i < mmd->segments_len; i++) { + + if (mmd->segments[i].seg_end < mmd->segments[i].seg_start) { + start = mmd->segments[i].seg_end; + end = mmd->segments[i].seg_start; + } + else { + start = mmd->segments[i].seg_start; + end = mmd->segments[i].seg_end; + } + for (int a = 0; a < mmd->segments[i].seg_repeat; a++) { + switch (mmd->segments[i].seg_mode) { + case GP_TIME_SEG_MODE_NORMAL: + for (int b = 0; b < end - start + 1; b++) { + segment_arr[frame_key] = start + b; + frame_key++; + } + break; + case GP_TIME_SEG_MODE_REVERSE: + for (int b = 0; b < end - start + 1; b++) { + segment_arr[frame_key] = end - b; + frame_key++; + } + break; + case GP_TIME_SEG_MODE_PINGPONG: + for (int b = 0; b < end - start; b++) { + segment_arr[frame_key] = start + b; + frame_key++; + } + for (int b = 0; b < end - start; b++) { + segment_arr[frame_key] = end - b; + frame_key++; + if (a == mmd->segments[i].seg_repeat - 1 && b == end - start - 1) { + segment_arr[frame_key] = start; + frame_key++; + } + } + break; + } + } + } + + if ((mmd->flag & GP_TIME_KEEP_LOOP) == 0) { + if ((cfra + offset - 1) < sequence_length) { + nfra = segment_arr[(cfra - 1 + offset)]; + } + else { + nfra = segment_arr[frame_key - 1]; + } + } + else { + nfra = segment_arr[(cfra - 1 + offset) % sequence_length]; + } + + MEM_freeN(segment_arr); + } + } + return nfra; } -static void panel_draw(const bContext *UNUSED(C), Panel *panel) +static void segment_list_item(struct uiList *UNUSED(ui_list), + struct bContext *UNUSED(C), + struct uiLayout *layout, + struct PointerRNA *UNUSED(idataptr), + struct PointerRNA *itemptr, + int UNUSED(icon), + struct PointerRNA *UNUSED(active_dataptr), + const char *UNUSED(active_propname), + int UNUSED(index), + int UNUSED(flt_flag)) +{ + uiLayout *row = uiLayoutRow(layout, true); + uiItemR(row, itemptr, "name", UI_ITEM_R_NO_BG, "", ICON_NONE); +} +static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + TimeGpencilModifierData *mmd = (TimeGpencilModifierData *)md; + + walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); +} +static void panel_draw(const bContext *C, Panel *panel) { uiLayout *row, *col; uiLayout *layout = panel->layout; @@ -175,6 +311,56 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetActive(row, mode != GP_TIME_MODE_FIX); uiItemR(row, ptr, "use_keep_loop", 0, NULL, ICON_NONE); + if (mode == GP_TIME_MODE_CHAIN) { + + uiLayout *row = uiLayoutRow(layout, false); + uiLayoutSetPropSep(row, false); + + uiTemplateList(row, + (bContext *)C, + "MOD_UL_time_segment", + "", + ptr, + "segments", + ptr, + "segment_active_index", + NULL, + 3, + 10, + 0, + 1, + UI_TEMPLATE_LIST_FLAG_NONE); + + uiLayout *col = uiLayoutColumn(row, false); + uiLayoutSetContextPointer(col, "modifier", ptr); + + uiLayout *sub = uiLayoutColumn(col, true); + uiItemO(sub, "", ICON_ADD, "GPENCIL_OT_time_segment_add"); + uiItemO(sub, "", ICON_REMOVE, "GPENCIL_OT_time_segment_remove"); + uiItemS(col); + sub = uiLayoutColumn(col, true); + uiItemEnumO_string(sub, "", ICON_TRIA_UP, "GPENCIL_OT_time_segment_move", "type", "UP"); + uiItemEnumO_string(sub, "", ICON_TRIA_DOWN, "GPENCIL_OT_time_segment_move", "type", "DOWN"); + + TimeGpencilModifierData *gpmd = ptr->data; + if (gpmd->segment_active_index >= 0 && gpmd->segment_active_index < gpmd->segments_len) { + PointerRNA ds_ptr; + RNA_pointer_create(ptr->owner_id, + &RNA_TimeGpencilModifierSegment, + &gpmd->segments[gpmd->segment_active_index], + &ds_ptr); + + sub = uiLayoutColumn(layout, true); + uiItemR(sub, &ds_ptr, "seg_mode", 0, NULL, ICON_NONE); + sub = uiLayoutColumn(layout, true); + uiItemR(sub, &ds_ptr, "seg_start", 0, NULL, ICON_NONE); + uiItemR(sub, &ds_ptr, "seg_end", 0, NULL, ICON_NONE); + uiItemR(sub, &ds_ptr, "seg_repeat", 0, NULL, ICON_NONE); + } + + gpencil_modifier_panel_end(layout, ptr); + } + gpencil_modifier_panel_end(layout, ptr); } @@ -186,7 +372,7 @@ static void custom_range_header_draw(const bContext *UNUSED(C), Panel *panel) int mode = RNA_enum_get(ptr, "mode"); - uiLayoutSetActive(layout, mode != GP_TIME_MODE_FIX); + uiLayoutSetActive(layout, (mode != GP_TIME_MODE_FIX && mode != GP_TIME_MODE_CHAIN)); uiItemR(layout, ptr, "use_custom_frame_range", 0, NULL, ICON_NONE); } @@ -201,9 +387,9 @@ static void custom_range_panel_draw(const bContext *UNUSED(C), Panel *panel) int mode = RNA_enum_get(ptr, "mode"); uiLayoutSetPropSep(layout, true); - - uiLayoutSetActive( - layout, (mode != GP_TIME_MODE_FIX) && (RNA_boolean_get(ptr, "use_custom_frame_range"))); + uiLayoutSetActive(layout, + (mode != GP_TIME_MODE_FIX && mode != GP_TIME_MODE_CHAIN) && + (RNA_boolean_get(ptr, "use_custom_frame_range"))); col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "frame_start", 0, IFACE_("Frame Start"), ICON_NONE); @@ -227,6 +413,11 @@ static void panelRegister(ARegionType *region_type) panel_type); gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); + + uiListType *list_type = MEM_callocN(sizeof(uiListType), "time modifier segment uilist"); + strcpy(list_type->idname, "MOD_UL_time_segment"); + list_type->draw_item = segment_list_item; + WM_uilisttype_add(list_type); } GpencilModifierTypeInfo modifierType_Gpencil_Time = { @@ -244,11 +435,11 @@ GpencilModifierTypeInfo modifierType_Gpencil_Time = { /* remapTime */ remapTime, /* initData */ initData, - /* freeData */ NULL, + /* freeData */ freeData, /* isDisabled */ NULL, /* updateDepsgraph */ NULL, /* dependsOnTime */ NULL, - /* foreachIDLink */ NULL, + /* foreachIDLink */ foreachIDLink, /* foreachTexLink */ NULL, /* panelRegister */ panelRegister, }; diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 56e317adca5..8078283df7a 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -262,6 +262,18 @@ .mode = 0, \ .sfra = 1, \ .efra = 250, \ + .segments = NULL, \ + .segments_len = 1, \ + .segment_active_index = 0, \ + } + + #define _DNA_DEFAULT_TimeGpencilModifierSegment \ + { \ + .name = "", \ + .seg_start = 1, \ + .seg_end = 2, \ + .seg_mode = 0, \ + .seg_repeat = 1, \ } #define _DNA_DEFAULT_TintGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index 629be51893a..0932b7107bb 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -202,8 +202,18 @@ typedef enum eThickGpencil_Flag { GP_THICK_WEIGHT_FACTOR = (1 << 7), } eThickGpencil_Flag; +typedef struct TimeGpencilModifierSegment { + char name[64]; + /* For path reference. */ + struct TimeGpencilModifierData *gpmd; + int seg_start; + int seg_end; + int seg_mode; + int seg_repeat; +} TimeGpencilModifierSegment; typedef struct TimeGpencilModifierData { GpencilModifierData modifier; + struct Material *material; /** Layer name. */ char layername[64]; /** Custom index for passes. */ @@ -216,7 +226,13 @@ typedef struct TimeGpencilModifierData { int mode; /** Start and end frame for custom range. */ int sfra, efra; + char _pad[4]; + + TimeGpencilModifierSegment *segments; + int segments_len; + int segment_active_index; + } TimeGpencilModifierData; typedef enum eTimeGpencil_Flag { @@ -231,8 +247,15 @@ typedef enum eTimeGpencil_Mode { GP_TIME_MODE_REVERSE = 1, GP_TIME_MODE_FIX = 2, GP_TIME_MODE_PINGPONG = 3, + GP_TIME_MODE_CHAIN = 4, } eTimeGpencil_Mode; +typedef enum eTimeGpencil_Seg_Mode { + GP_TIME_SEG_MODE_NORMAL = 0, + GP_TIME_SEG_MODE_REVERSE = 1, + GP_TIME_SEG_MODE_PINGPONG = 2, +} eTimeGpencil_Seg_Mode; + typedef enum eModifyColorGpencil_Flag { GP_MODIFY_COLOR_BOTH = 0, GP_MODIFY_COLOR_STROKE = 1, diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 3cd0cbcc3d6..309756ff2da 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -309,6 +309,7 @@ SDNA_DEFAULT_DECL_STRUCT(SubdivGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(TextureGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(ThickGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(TimeGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(TimeGpencilModifierSegment); SDNA_DEFAULT_DECL_STRUCT(TintGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(WeightProxGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(WeightAngleGpencilModifierData); @@ -551,6 +552,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(ThickGpencilModifierData), SDNA_DEFAULT_DECL(TimeGpencilModifierData), SDNA_DEFAULT_DECL(TintGpencilModifierData), + SDNA_DEFAULT_DECL(TimeGpencilModifierSegment), SDNA_DEFAULT_DECL(WeightAngleGpencilModifierData), SDNA_DEFAULT_DECL(WeightProxGpencilModifierData), SDNA_DEFAULT_DECL(LineartGpencilModifierData), diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 0a400d3bcef..ae8208ed0d8 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -201,7 +201,19 @@ static const EnumPropertyItem rna_enum_time_mode_items[] = { {GP_TIME_MODE_NORMAL, "NORMAL", 0, "Regular", "Apply offset in usual animation direction"}, {GP_TIME_MODE_REVERSE, "REVERSE", 0, "Reverse", "Apply offset in reverse animation direction"}, {GP_TIME_MODE_FIX, "FIX", 0, "Fixed Frame", "Keep frame and do not change with time"}, - {GP_TIME_MODE_PINGPONG, "PINGPONG", 0, "Ping Pong", "Loop back and forth"}, + {GP_TIME_MODE_PINGPONG, "PINGPONG", 0, "Ping Pong", "Loop back and forth starting in reverse"}, + {GP_TIME_MODE_CHAIN, "CHAIN", 0, "Chain", "List of chained animation segments"}, + {0, NULL, 0, NULL, NULL}, +}; + +static const EnumPropertyItem rna_enum_time_seg_mode_items[] = { + {GP_TIME_SEG_MODE_NORMAL, "NORMAL", 0, "Regular", "Apply offset in usual animation direction"}, + {GP_TIME_SEG_MODE_REVERSE, + "REVERSE", + 0, + "Reverse", + "Apply offset in reverse animation direction"}, + {GP_TIME_SEG_MODE_PINGPONG, "PINGPONG", 0, "Ping Pong", "Loop back and forth"}, {0, NULL, 0, NULL, NULL}, }; @@ -791,7 +803,32 @@ static void rna_GpencilDash_segments_begin(CollectionPropertyIterator *iter, Poi iter, dmd->segments, sizeof(DashGpencilModifierSegment), dmd->segments_len, false, NULL); } +static void rna_GpencilTime_segments_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)ptr->data; + rna_iterator_array_begin( + iter, gpmd->segments, sizeof(TimeGpencilModifierSegment), gpmd->segments_len, false, NULL); +} + +static char *rna_TimeGpencilModifierSegment_path(const PointerRNA *ptr) +{ + TimeGpencilModifierSegment *ds = (TimeGpencilModifierSegment *)ptr->data; + + TimeGpencilModifierData *gpmd = (TimeGpencilModifierData *)ds->gpmd; + + BLI_assert(gpmd != NULL); + + char name_esc[sizeof(gpmd->modifier.name) * 2]; + BLI_str_escape(name_esc, gpmd->modifier.name, sizeof(name_esc)); + + char ds_name_esc[sizeof(ds->name) * 2]; + BLI_str_escape(ds_name_esc, ds->name, sizeof(ds_name_esc)); + + return BLI_sprintfN("grease_pencil_modifiers[\"%s\"].segments[\"%s\"]", name_esc, ds_name_esc); +} + static char *rna_DashGpencilModifierSegment_path(const PointerRNA *ptr) + { const DashGpencilModifierSegment *ds = (DashGpencilModifierSegment *)ptr->data; @@ -819,6 +856,17 @@ static bool dash_segment_name_exists_fn(void *arg, const char *name) return false; } +static bool time_segment_name_exists_fn(void *arg, const char *name) +{ + const TimeGpencilModifierData *gpmd = (const TimeGpencilModifierData *)arg; + for (int i = 0; i < gpmd->segments_len; i++) { + if (STREQ(gpmd->segments[i].name, name) && gpmd->segments[i].name != name) { + return true; + } + } + return false; +} + static void rna_DashGpencilModifierSegment_name_set(PointerRNA *ptr, const char *value) { DashGpencilModifierSegment *ds = ptr->data; @@ -842,6 +890,29 @@ static void rna_DashGpencilModifierSegment_name_set(PointerRNA *ptr, const char BKE_animdata_fix_paths_rename_all(NULL, prefix, oldname, ds->name); } +static void rna_TimeGpencilModifierSegment_name_set(PointerRNA *ptr, const char *value) +{ + TimeGpencilModifierSegment *ds = ptr->data; + + char oldname[sizeof(ds->name)]; + BLI_strncpy(oldname, ds->name, sizeof(ds->name)); + + BLI_strncpy_utf8(ds->name, value, sizeof(ds->name)); + + BLI_assert(ds->gpmd != NULL); + BLI_uniquename_cb( + time_segment_name_exists_fn, ds->gpmd, "Segment", '.', ds->name, sizeof(ds->name)); + + char name_esc[sizeof(ds->gpmd->modifier.name) * 2]; + BLI_str_escape(name_esc, ds->gpmd->modifier.name, sizeof(name_esc)); + + char prefix[36 + sizeof(name_esc) + 1]; + SNPRINTF(prefix, "grease_pencil_modifiers[\"%s\"].segments", name_esc); + + /* Fix all the animation data which may link to this. */ + BKE_animdata_fix_paths_rename_all(NULL, prefix, oldname, ds->name); +} + static int rna_ShrinkwrapGpencilModifier_face_cull_get(PointerRNA *ptr) { ShrinkwrapGpencilModifierData *swm = (ShrinkwrapGpencilModifierData *)ptr->data; @@ -1718,6 +1789,38 @@ static void rna_def_modifier_gpenciltime(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; + srna = RNA_def_struct(brna, "TimeGpencilModifierSegment", NULL); + RNA_def_struct_ui_text(srna, "Time Modifier Segment", "Configuration for a single dash segment"); + RNA_def_struct_sdna(srna, "TimeGpencilModifierSegment"); + RNA_def_struct_path_func(srna, "rna_TimeGpencilModifierSegment_path"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Name", "Name of the dash segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_TimeGpencilModifierSegment_name_set"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, NULL); + RNA_def_struct_name_property(srna, prop); + + prop = RNA_def_property(srna, "seg_start", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 0, INT16_MAX); + RNA_def_property_ui_text(prop, "Frame Start", "First frame of the segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "seg_end", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 0, INT16_MAX); + RNA_def_property_ui_text(prop, "End", "Last frame of the segment"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "seg_repeat", PROP_INT, PROP_NONE); + RNA_def_property_range(prop, 0, INT16_MAX); + RNA_def_property_ui_text(prop, "Repeat", "Number of cycle repeats"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "seg_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "seg_mode"); + RNA_def_property_enum_items(prop, rna_enum_time_seg_mode_items); + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); srna = RNA_def_struct(brna, "TimeGpencilModifier", "GpencilModifier"); RNA_def_struct_ui_text(srna, "Time Offset Modifier", "Time offset modifier"); @@ -1726,6 +1829,24 @@ static void rna_def_modifier_gpenciltime(BlenderRNA *brna) RNA_define_lib_overridable(true); + prop = RNA_def_property(srna, "segments", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "TimeGpencilModifierSegment"); + RNA_def_property_collection_sdna(prop, NULL, "segments", NULL); + RNA_def_property_collection_funcs(prop, + "rna_GpencilTime_segments_begin", + "rna_iterator_array_next", + "rna_iterator_array_end", + "rna_iterator_array_get", + NULL, + NULL, + NULL, + NULL); + RNA_def_property_ui_text(prop, "Segments", ""); + + prop = RNA_def_property(srna, "segment_active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Active Dash Segment Index", "Active index in the segment list"); + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "mode"); RNA_def_property_enum_items(prop, rna_enum_time_mode_items); |