Blender  V3.3
MOD_gpencildash.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2021 Blender Foundation. */
3 
8 #include <stdio.h>
9 #include <string.h>
10 
11 #include "BLI_listbase.h"
12 #include "BLI_math_vector.h"
13 #include "BLI_string.h"
14 
15 #include "DNA_defaults.h"
17 #include "DNA_gpencil_types.h"
18 #include "DNA_meshdata_types.h"
19 #include "DNA_object_types.h"
20 #include "DNA_screen_types.h"
21 
22 #include "BKE_gpencil.h"
23 #include "BKE_gpencil_geom.h"
24 #include "BKE_gpencil_modifier.h"
25 #include "BKE_lib_query.h"
26 #include "BKE_main.h"
27 #include "BKE_modifier.h"
28 #include "BKE_screen.h"
29 
30 #include "MEM_guardedalloc.h"
31 
32 #include "UI_interface.h"
33 #include "UI_resources.h"
34 
35 #include "RNA_access.h"
36 #include "RNA_prototypes.h"
37 
38 #include "BLT_translation.h"
39 
41 #include "MOD_gpencil_ui_common.h"
42 #include "MOD_gpencil_util.h"
43 
44 #include "DEG_depsgraph.h"
45 
46 #include "WM_api.h"
47 
48 static void initData(GpencilModifierData *md)
49 {
51 
53 
55 
57  ds->dmd = dmd;
58  BLI_strncpy(ds->name, DATA_("Segment"), sizeof(ds->name));
59 
60  dmd->segments = ds;
61 }
62 
63 static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
64 {
66  const DashGpencilModifierData *dmd_src = (const DashGpencilModifierData *)md;
67 
69 
70  dmd->segments = MEM_dupallocN(dmd_src->segments);
71 }
72 
73 static void freeData(GpencilModifierData *md)
74 {
76 
77  MEM_SAFE_FREE(dmd->segments);
78 }
79 
86 static int real_gap(const DashGpencilModifierSegment *ds)
87 {
88  return ds->gap - 1;
89 }
90 
91 static bool stroke_dash(const bGPDstroke *gps,
92  const DashGpencilModifierData *dmd,
93  ListBase *r_strokes)
94 {
95  int new_stroke_offset = 0;
96  int trim_start = 0;
97 
98  int sequence_length = 0;
99  for (int i = 0; i < dmd->segments_len; i++) {
100  sequence_length += dmd->segments[i].dash + real_gap(&dmd->segments[i]);
101  }
102  if (sequence_length < 1) {
103  /* This means the whole segment has no length, can't do dot-dash. */
104  return false;
105  }
106 
107  const DashGpencilModifierSegment *const first_segment = &dmd->segments[0];
108  const DashGpencilModifierSegment *const last_segment = &dmd->segments[dmd->segments_len - 1];
109  const DashGpencilModifierSegment *ds = first_segment;
110 
111  /* Determine starting configuration using offset. */
112  int offset_trim = dmd->dash_offset;
113  while (offset_trim < 0) {
114  ds = (ds == first_segment) ? last_segment : ds - 1;
115  offset_trim += ds->dash + real_gap(ds);
116  }
117 
118  /* This segment is completely removed from view by the index offset, ignore it. */
119  while (ds->dash + real_gap(ds) < offset_trim) {
120  offset_trim -= ds->dash + real_gap(ds);
121  ds = (ds == last_segment) ? first_segment : ds + 1;
122  }
123 
124  /* This segment is partially visible at the beginning of the stroke. */
125  if (ds->dash > offset_trim) {
126  trim_start = offset_trim;
127  }
128  else {
129  /* This segment is not visible but the gap immediately after this segment is partially visible,
130  * use next segment's dash. */
131  new_stroke_offset += ds->dash + real_gap(ds) - offset_trim;
132  ds = (ds == last_segment) ? first_segment : ds + 1;
133  }
134 
135  while (new_stroke_offset < gps->totpoints - 1) {
136  const int seg = ds->dash - trim_start;
137  if (!(seg || real_gap(ds))) {
138  ds = (ds == last_segment) ? first_segment : ds + 1;
139  continue;
140  }
141 
142  const int size = MIN2(gps->totpoints - new_stroke_offset, seg);
143  if (size == 0) {
144  continue;
145  }
146 
148  ds->mat_nr < 0 ? gps->mat_nr : ds->mat_nr, size, gps->thickness);
149  stroke->runtime.gps_orig = gps->runtime.gps_orig;
150  if (ds->flag & GP_DASH_USE_CYCLIC) {
151  stroke->flag |= GP_STROKE_CYCLIC;
152  }
153 
154  for (int is = 0; is < size; is++) {
155  bGPDspoint *p = &gps->points[new_stroke_offset + is];
156  stroke->points[is].x = p->x;
157  stroke->points[is].y = p->y;
158  stroke->points[is].z = p->z;
159  stroke->points[is].pressure = p->pressure * ds->radius;
160  stroke->points[is].strength = p->strength * ds->opacity;
161  /* Assign original point pointers. */
162  stroke->points[is].runtime.idx_orig = p->runtime.idx_orig;
163  stroke->points[is].runtime.pt_orig = p->runtime.pt_orig;
164  copy_v4_v4(stroke->points[is].vert_color, p->vert_color);
165  }
166  BLI_addtail(r_strokes, stroke);
167 
168  if (gps->dvert) {
169  BKE_gpencil_dvert_ensure(stroke);
170  for (int di = 0; di < stroke->totpoints; di++) {
171  MDeformVert *dv = &gps->dvert[new_stroke_offset + di];
172  if (dv && dv->totweight && dv->dw) {
174  __func__);
175  memcpy(dw, dv->dw, sizeof(MDeformWeight) * dv->totweight);
176  stroke->dvert[di].dw = dw;
177  stroke->dvert[di].totweight = dv->totweight;
178  stroke->dvert[di].flag = dv->flag;
179  }
180  }
181  }
182 
183  new_stroke_offset += seg + real_gap(ds);
184  ds = (ds == last_segment) ? first_segment : ds + 1;
185  trim_start = 0;
186  }
187 
188  return true;
189 }
190 
192  Object *ob, bGPDlayer *gpl, bGPdata *gpd, bGPDframe *gpf, DashGpencilModifierData *dmd)
193 {
194  if (dmd->segments_len == 0) {
195  return;
196  }
197 
198  ListBase result = {NULL, NULL};
199 
202  dmd->layername,
203  dmd->material,
204  dmd->pass_index,
205  dmd->layer_pass,
206  1,
207  gpl,
208  gps,
213  if (stroke_dash(gps, dmd, &result)) {
214  BLI_remlink(&gpf->strokes, gps);
216  }
217  }
218  }
219  bGPDstroke *gps_dash;
220  while ((gps_dash = BLI_pophead(&result))) {
221  BLI_addtail(&gpf->strokes, gps_dash);
222  BKE_gpencil_stroke_geometry_update(gpd, gps_dash);
223  }
224 }
225 
226 static void bakeModifier(Main *UNUSED(bmain),
229  Object *ob)
230 {
231  bGPdata *gpd = ob->data;
232 
233  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
234  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
235  apply_dash_for_frame(ob, gpl, gpd, gpf, (DashGpencilModifierData *)md);
236  }
237  }
238 }
239 
240 /* -------------------------------- */
241 
242 static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
243 {
245 
246  int sequence_length = 0;
247  for (int i = 0; i < dmd->segments_len; i++) {
248  sequence_length += dmd->segments[i].dash + real_gap(&dmd->segments[i]);
249  }
250  /* This means the whole segment has no length, can't do dot-dash. */
251  return sequence_length < 1;
252 }
253 
254 /* Generic "generateStrokes" callback */
256 {
257  bGPdata *gpd = ob->data;
258 
259  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
261  bGPDframe *gpf = gpl->actframe;
262  if (gpf == NULL) {
263  continue;
264  }
265  apply_dash_for_frame(ob, gpl, gpd, gpf, (DashGpencilModifierData *)md);
266  }
267 }
268 
269 static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
270 {
272 
273  walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER);
274 }
275 
276 static void segment_list_item(struct uiList *UNUSED(ui_list),
277  struct bContext *UNUSED(C),
278  struct uiLayout *layout,
279  struct PointerRNA *UNUSED(idataptr),
280  struct PointerRNA *itemptr,
281  int UNUSED(icon),
282  struct PointerRNA *UNUSED(active_dataptr),
283  const char *UNUSED(active_propname),
284  int UNUSED(index),
285  int UNUSED(flt_flag))
286 {
287  uiLayout *row = uiLayoutRow(layout, true);
288  uiItemR(row, itemptr, "name", UI_ITEM_R_NO_BG, "", ICON_NONE);
289 }
290 
291 static void panel_draw(const bContext *C, Panel *panel)
292 {
293  uiLayout *layout = panel->layout;
294 
296 
297  uiLayoutSetPropSep(layout, true);
298 
299  uiItemR(layout, ptr, "dash_offset", 0, NULL, ICON_NONE);
300 
301  uiLayout *row = uiLayoutRow(layout, false);
302  uiLayoutSetPropSep(row, false);
303 
304  uiTemplateList(row,
305  (bContext *)C,
306  "MOD_UL_dash_segment",
307  "",
308  ptr,
309  "segments",
310  ptr,
311  "segment_active_index",
312  NULL,
313  3,
314  10,
315  0,
316  1,
318 
319  uiLayout *col = uiLayoutColumn(row, false);
320  uiLayout *sub = uiLayoutColumn(col, true);
321  uiItemO(sub, "", ICON_ADD, "GPENCIL_OT_segment_add");
322  uiItemO(sub, "", ICON_REMOVE, "GPENCIL_OT_segment_remove");
323  uiItemS(col);
324  sub = uiLayoutColumn(col, true);
325  uiItemEnumO_string(sub, "", ICON_TRIA_UP, "GPENCIL_OT_segment_move", "type", "UP");
326  uiItemEnumO_string(sub, "", ICON_TRIA_DOWN, "GPENCIL_OT_segment_move", "type", "DOWN");
327 
329 
330  if (dmd->segment_active_index >= 0 && dmd->segment_active_index < dmd->segments_len) {
331  PointerRNA ds_ptr;
333  &RNA_DashGpencilModifierSegment,
334  &dmd->segments[dmd->segment_active_index],
335  &ds_ptr);
336 
337  sub = uiLayoutColumn(layout, true);
338  uiItemR(sub, &ds_ptr, "dash", 0, NULL, ICON_NONE);
339  uiItemR(sub, &ds_ptr, "gap", 0, NULL, ICON_NONE);
340 
341  sub = uiLayoutColumn(layout, false);
342  uiItemR(sub, &ds_ptr, "radius", 0, NULL, ICON_NONE);
343  uiItemR(sub, &ds_ptr, "opacity", 0, NULL, ICON_NONE);
344  uiItemR(sub, &ds_ptr, "material_index", 0, NULL, ICON_NONE);
345  uiItemR(sub, &ds_ptr, "use_cyclic", 0, NULL, ICON_NONE);
346  }
347 
349 }
350 
351 static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
352 {
353  gpencil_modifier_masking_panel_draw(panel, true, false);
354 }
355 
356 static void panelRegister(ARegionType *region_type)
357 {
359  region_type, eGpencilModifierType_Dash, panel_draw);
361  region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type);
362 
363  uiListType *list_type = MEM_callocN(sizeof(uiListType), "dash modifier segment uilist");
364  strcpy(list_type->idname, "MOD_UL_dash_segment");
365  list_type->draw_item = segment_list_item;
366  WM_uilisttype_add(list_type);
367 }
368 
370  /* name */ N_("Dot Dash"),
371  /* structName */ "DashGpencilModifierData",
372  /* structSize */ sizeof(DashGpencilModifierData),
375 
376  /* copyData */ copyData,
377 
378  /* deformStroke */ NULL,
379  /* generateStrokes */ generateStrokes,
380  /* bakeModifier */ bakeModifier,
381  /* remapTime */ NULL,
382 
383  /* initData */ initData,
384  /* freeData */ freeData,
385  /* isDisabled */ isDisabled,
386  /* updateDepsgraph */ NULL,
387  /* dependsOnTime */ NULL,
388  /* foreachIDLink */ foreachIDLink,
389  /* foreachTexLink */ NULL,
390  /* panelRegister */ panelRegister,
391 };
void BKE_gpencil_frame_active_set(struct Depsgraph *depsgraph, struct bGPdata *gpd)
void BKE_gpencil_dvert_ensure(struct bGPDstroke *gps)
Definition: gpencil.c:1891
struct bGPDstroke * BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness)
Definition: gpencil.c:756
void BKE_gpencil_free_stroke(struct bGPDstroke *gps)
Definition: gpencil.c:391
void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps)
void BKE_gpencil_modifier_copydata_generic(const struct GpencilModifierData *md_src, struct GpencilModifierData *md_dst)
@ eGpencilModifierTypeFlag_SupportsEditmode
@ eGpencilModifierTypeType_Gpencil
@ IDWALK_CB_USER
Definition: BKE_lib_query.h:73
void(* IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag)
Definition: BKE_modifier.h:107
#define BLI_assert(a)
Definition: BLI_assert.h:46
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:221
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
MINLINE void copy_v4_v4(float r[4], const float a[4])
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
#define UNUSED(x)
#define MIN2(a, b)
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define DATA_(msgid)
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
#define DNA_struct_default_get(struct_name)
Definition: DNA_defaults.h:29
#define DNA_struct_default_alloc(struct_name)
Definition: DNA_defaults.h:32
@ GP_LENGTH_INVERT_MATERIAL
@ GP_LENGTH_INVERT_LAYER
@ GP_LENGTH_INVERT_LAYERPASS
struct DashGpencilModifierData DashGpencilModifierData
@ eGpencilModifierType_Dash
@ GP_STROKE_CYCLIC
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
PointerRNA * gpencil_modifier_panel_get_property_pointers(Panel *panel, PointerRNA *r_ob_ptr)
void gpencil_modifier_masking_panel_draw(Panel *panel, bool use_material, bool use_vertex)
void gpencil_modifier_panel_end(uiLayout *layout, PointerRNA *ptr)
PanelType * gpencil_modifier_subpanel_register(ARegionType *region_type, const char *name, const char *label, PanelDrawFn draw_header, PanelDrawFn draw, PanelType *parent)
PanelType * gpencil_modifier_panel_register(ARegionType *region_type, GpencilModifierType type, PanelDrawFn draw)
bool is_stroke_affected_by_modifier(Object *ob, char *mlayername, Material *material, const int mpassindex, const int gpl_passindex, const int minpoints, bGPDlayer *gpl, bGPDstroke *gps, const bool inv1, const bool inv2, const bool inv3, const bool inv4)
static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams))
static void freeData(GpencilModifierData *md)
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))
GpencilModifierTypeInfo modifierType_Gpencil_Dash
static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
static void apply_dash_for_frame(Object *ob, bGPDlayer *gpl, bGPdata *gpd, bGPDframe *gpf, DashGpencilModifierData *dmd)
static int real_gap(const DashGpencilModifierSegment *ds)
static void bakeModifier(Main *UNUSED(bmain), Depsgraph *UNUSED(depsgraph), GpencilModifierData *md, Object *ob)
static void panelRegister(ARegionType *region_type)
static void initData(GpencilModifierData *md)
static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob)
static bool stroke_dash(const bGPDstroke *gps, const DashGpencilModifierData *dmd, ListBase *r_strokes)
static void panel_draw(const bContext *C, Panel *panel)
#define C
Definition: RandGen.cpp:25
void uiItemEnumO_string(uiLayout *layout, const char *name, int icon, const char *opname, const char *propname, const char *value)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemS(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
@ UI_ITEM_R_NO_BG
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname)
void uiTemplateList(uiLayout *layout, struct bContext *C, const char *listtype_name, const char *list_id, struct PointerRNA *dataptr, const char *propname, struct PointerRNA *active_dataptr, const char *active_propname, const char *item_dyntip_propname, int rows, int maxrows, int layout_type, int columns, enum uiTemplateListFlags flags)
@ UI_TEMPLATE_LIST_FLAG_NONE
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
const Depsgraph * depsgraph
uint col
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:28
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void RNA_pointer_create(ID *id, StructRNA *type, void *data, PointerRNA *r_ptr)
Definition: rna_access.c:136
DashGpencilModifierSegment * segments
struct DashGpencilModifierData * dmd
Definition: DNA_ID.h:368
struct MDeformWeight * dw
Definition: BKE_main.h:121
void * data
struct uiLayout * layout
void * data
Definition: RNA_types.h:38
struct ID * owner_id
Definition: RNA_types.h:36
ListBase strokes
struct bGPDspoint * pt_orig
bGPDspoint_Runtime runtime
float vert_color[4]
struct bGPDstroke * gps_orig
bGPDspoint * points
bGPDstroke_Runtime runtime
struct MDeformVert * dvert
ListBase layers
char idname[BKE_ST_MAXNAME]
Definition: BKE_screen.h:321
uiListDrawItemFunc draw_item
Definition: BKE_screen.h:323
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480
bool WM_uilisttype_add(uiListType *ult)