Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleš Jelovčan <frogstomp>2022-09-27 18:47:01 +0300
committerAntonio Vazquez <blendergit@gmail.com>2022-09-27 18:55:43 +0300
commitb0d70a9c80402e8b7af173a76e9a8b6050598825 (patch)
treecc2d98fe527473e16052ab09adf6ed5615a07f61
parent12fdf9069abe3cd2250a9efec6e059eb85ec59d8 (diff)
Gpencil: Time Offset modifier new Chain mode
This patch adds 5th mode to Time offset modifier, which should allow to create time segments list. This will allow users to chain together multiple time ranges in 4 modes: - Forward - Backwards - Pingpong - Reverse Pingpong It also comes with additional Repeat parameter which specifies number of times particular segment should run. The mechanic of it is transforming initial parameters into array of frames which are mapped to existing cfra (current frame) value. Prototype : https://jsfiddle.net/ha2sjw8p/3/ This is also closely aligned to community request: https://blender.community/c/rightclickselect/Txhbbc/ This should allow creation of complex animations like dancing, which consists of repeating loops and transitions to the next. One important side effect of this is dramatically reduced file sizes, as user no longer needs to copy paste keyframes. Reviewed By: antoniov, mendio, pepeland Differential Revision: https://developer.blender.org/D15052
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c12
-rw-r--r--source/blender/editors/object/object_gpencil_modifier.c236
-rw-r--r--source/blender/editors/object/object_intern.h4
-rw-r--r--source/blender/editors/object/object_ops.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c211
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_defaults.h12
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h23
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c2
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c123
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);