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:
-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);