Blender  V3.3
MOD_gpencilbuild.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2017 Blender Foundation. */
3 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include "MEM_guardedalloc.h"
13 
14 #include "BLI_listbase.h"
15 #include "BLI_math_base.h"
16 #include "BLI_math_vector.h"
17 #include "BLI_sort.h"
18 #include "BLI_utildefines.h"
19 
20 #include "BLT_translation.h"
21 
22 #include "DNA_defaults.h"
24 #include "DNA_gpencil_types.h"
25 #include "DNA_meshdata_types.h"
26 #include "DNA_object_types.h"
27 #include "DNA_scene_types.h"
28 #include "DNA_screen_types.h"
29 
30 #include "BKE_context.h"
31 #include "BKE_deform.h"
32 #include "BKE_gpencil.h"
33 #include "BKE_gpencil_geom.h"
34 #include "BKE_gpencil_modifier.h"
35 #include "BKE_lib_query.h"
36 #include "BKE_modifier.h"
37 #include "BKE_screen.h"
38 
39 #include "UI_interface.h"
40 #include "UI_resources.h"
41 
42 #include "RNA_access.h"
43 
44 #include "DEG_depsgraph.h"
45 #include "DEG_depsgraph_query.h"
46 
48 #include "MOD_gpencil_ui_common.h"
49 
50 static void initData(GpencilModifierData *md)
51 {
53 
54  BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier));
55 
57 }
58 
59 static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
60 {
62 }
63 
65 {
66  return true;
67 }
68 
69 /* ******************************************** */
70 /* Build Modifier - Stroke generation logic
71  *
72  * There are two modes for how the strokes are sequenced (at a macro-level):
73  * - Sequential Mode - Strokes appear/disappear one after the other. Only a single one changes at a
74  * time.
75  * - Concurrent Mode - Multiple strokes appear/disappear at once.
76  *
77  * Assumptions:
78  * - Stroke points are generally equally spaced. This implies that we can just add/remove points,
79  * without worrying about distances between them / adding extra interpolated points between
80  * an visible point and one about to be added/removed (or any similar tapering effects).
81  *
82  * - All strokes present are fully visible (i.e. we don't have to ignore any)
83  */
84 
85 /* Remove a particular stroke */
86 static void clear_stroke(bGPDframe *gpf, bGPDstroke *gps)
87 {
88  BLI_remlink(&gpf->strokes, gps);
90 }
91 
92 /* Clear all strokes in frame */
94 {
95  bGPDstroke *gps, *gps_next;
96  for (gps = gpf->strokes.first; gps; gps = gps_next) {
97  gps_next = gps->next;
98  clear_stroke(gpf, gps);
99  }
101 }
102 
103 /* Reduce the number of points in the stroke
104  *
105  * NOTE: This won't be called if all points are present/removed
106  */
107 static void reduce_stroke_points(bGPdata *gpd,
108  bGPDframe *gpf,
109  bGPDstroke *gps,
110  const int points_num,
111  const eBuildGpencil_Transition transition)
112 {
113  if ((points_num == 0) || (gps->points == NULL)) {
114  clear_stroke(gpf, gps);
115  return;
116  }
117  bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * points_num, __func__);
118  MDeformVert *new_dvert = NULL;
119  if ((gps->dvert != NULL) && (points_num > 0)) {
120  new_dvert = MEM_callocN(sizeof(MDeformVert) * points_num, __func__);
121  }
122 
123  /* Which end should points be removed from. */
124  switch (transition) {
125  case GP_BUILD_TRANSITION_GROW: /* Show in forward order =
126  * Remove ungrown-points from end of stroke. */
127  case GP_BUILD_TRANSITION_SHRINK: /* Hide in reverse order =
128  * Remove dead-points from end of stroke. */
129  {
130  /* copy over point data */
131  memcpy(new_points, gps->points, sizeof(bGPDspoint) * points_num);
132  if ((gps->dvert != NULL) && (points_num > 0)) {
133  memcpy(new_dvert, gps->dvert, sizeof(MDeformVert) * points_num);
134 
135  /* free unused point weights */
136  for (int i = points_num; i < gps->totpoints; i++) {
137  MDeformVert *dvert = &gps->dvert[i];
139  }
140  }
141  break;
142  }
143 
144  /* Hide in forward order = Remove points from start of stroke */
146  /* points_num is the number of points left after reducing.
147  * We need to know how many to remove
148  */
149  const int offset = gps->totpoints - points_num;
150 
151  /* copy over point data */
152  memcpy(new_points, gps->points + offset, sizeof(bGPDspoint) * points_num);
153  if ((gps->dvert != NULL) && (points_num > 0)) {
154  memcpy(new_dvert, gps->dvert + offset, sizeof(MDeformVert) * points_num);
155 
156  /* free unused weights */
157  for (int i = 0; i < offset; i++) {
158  MDeformVert *dvert = &gps->dvert[i];
160  }
161  }
162  break;
163  }
164 
165  default:
166  printf("ERROR: Unknown transition %d in %s()\n", (int)transition, __func__);
167  break;
168  }
169 
170  /* replace stroke geometry */
171  MEM_SAFE_FREE(gps->points);
172  MEM_SAFE_FREE(gps->dvert);
173  gps->points = new_points;
174  gps->dvert = new_dvert;
175  gps->totpoints = points_num;
176 
177  /* Calc geometry data. */
179 }
180 
182  const int starting_index,
183  const int ending_index,
184  const float starting_weight,
185  const float ending_weight,
186  const int target_def_nr,
187  const eBuildGpencil_Transition transition,
188  const float thickness_strength,
189  const float opacity_strength)
190 {
191  MDeformVert *dvert;
192 
193  int range = ending_index - starting_index;
194  if (!range) {
195  range = 1;
196  }
197 
198  /* Which end should points be removed from */
199  switch (transition) {
200  /* Because starting_weight and ending_weight are set in correct order before calling this
201  * function, those three modes can use the same interpolation code. */
205  for (int i = starting_index; i <= ending_index; i++) {
206  float weight = interpf(
207  ending_weight, starting_weight, (float)(i - starting_index) / range);
208  if (target_def_nr >= 0) {
209  dvert = &gps->dvert[i];
210  MDeformWeight *dw = BKE_defvert_ensure_index(dvert, target_def_nr);
211  if (dw) {
212  dw->weight = weight;
213  CLAMP(dw->weight, 0.0f, 1.0f);
214  }
215  }
216  if (thickness_strength > 1e-5) {
217  gps->points[i].pressure *= interpf(weight, 1.0f, thickness_strength);
218  }
219  if (opacity_strength > 1e-5) {
220  gps->points[i].strength *= interpf(weight, 1.0f, opacity_strength);
221  }
222  }
223  break;
224  }
225 
226  default:
227  printf("ERROR: Unknown transition %d in %s()\n", (int)transition, __func__);
228  break;
229  }
230 }
231 
232 /* --------------------------------------------- */
233 
234 /* Stroke Data Table Entry - This represents one stroke being generated */
235 typedef struct tStrokeBuildDetails {
237 
238  /* Indices - first/last indices for the stroke's points (overall) */
240 
241  /* Number of points - Cache for more convenient access */
243 
244  /* Distance to control object, used to sort the strokes if set. */
245  float distance;
247 
248 static int cmp_stroke_build_details(const void *ps1, const void *ps2)
249 {
252  return p1->distance > p2->distance ? 1 : (p1->distance == p2->distance ? 0 : -1);
253 }
254 
255 /* Sequential and additive - Show strokes one after the other. */
256 static void build_sequential(Object *ob,
258  bGPdata *gpd,
259  bGPDframe *gpf,
260  const int target_def_nr,
261  float fac,
262  bool additive)
263 {
264  size_t tot_strokes = BLI_listbase_count(&gpf->strokes);
265  size_t start_stroke;
266  bGPDstroke *gps;
267  size_t i;
268 
269  /* 1) Determine which strokes to start with & total strokes to build. */
270 
271  if (additive) {
272  if (gpf->prev) {
273  start_stroke = BLI_listbase_count(&gpf->prev->strokes);
274  }
275  else {
276  start_stroke = 0;
277  }
278  if (start_stroke <= tot_strokes) {
279  tot_strokes = tot_strokes - start_stroke;
280  }
281  else {
282  start_stroke = 0;
283  }
284  }
285  else {
286  start_stroke = 0;
287  }
288 
289  /* 2) Compute proportion of time each stroke should occupy */
290  /* NOTE: This assumes that the total number of points won't overflow! */
291  tStrokeBuildDetails *table = MEM_callocN(sizeof(tStrokeBuildDetails) * tot_strokes, __func__);
292  size_t totpoints = 0;
293 
294  /* 2.1) First pass - Tally up points */
295  for (gps = BLI_findlink(&gpf->strokes, start_stroke), i = 0; gps; gps = gps->next, i++) {
296  tStrokeBuildDetails *cell = &table[i];
297 
298  cell->gps = gps;
299  cell->totpoints = gps->totpoints;
300 
301  totpoints += cell->totpoints;
302 
303  /* Compute distance to control object if set, and build according to that order. */
304  if (mmd->object) {
305  float sv1[3], sv2[3];
306  mul_v3_m4v3(sv1, ob->obmat, &gps->points[0].x);
307  mul_v3_m4v3(sv2, ob->obmat, &gps->points[gps->totpoints - 1].x);
308  float dist_l = len_v3v3(sv1, mmd->object->loc);
309  float dist_r = len_v3v3(sv2, mmd->object->loc);
310  if (dist_r < dist_l) {
312  cell->distance = dist_r;
313  }
314  else {
315  cell->distance = dist_l;
316  }
317  }
318  }
319 
320  if (mmd->object) {
321  qsort(table, tot_strokes, sizeof(tStrokeBuildDetails), cmp_stroke_build_details);
322  }
323 
324  /* 2.2) Second pass - Compute the overall indices for points */
325  for (i = 0; i < tot_strokes; i++) {
326  tStrokeBuildDetails *cell = &table[i];
327 
328  if (i == 0) {
329  cell->start_idx = 0;
330  }
331  else {
332  cell->start_idx = (cell - 1)->end_idx;
333  }
334  cell->end_idx = cell->start_idx + cell->totpoints - 1;
335  }
336 
337  /* 3) Determine the global indices for points that should be visible */
338  size_t first_visible = 0;
339  size_t last_visible = 0;
340  /* Need signed numbers because the representation of fading offset would exceed the beginning and
341  * the end of offsets. */
342  int fade_start = 0;
343  int fade_end = 0;
344 
345  bool fading_enabled = (mmd->flag & GP_BUILD_USE_FADING);
346 
347  float set_fade_fac = fading_enabled ? mmd->fade_fac : 0.0f;
348  float use_fac = interpf(1 + set_fade_fac, 0, fac);
349  float use_fade_fac = use_fac - set_fade_fac;
350  CLAMP(use_fade_fac, 0.0f, 1.0f);
351 
352  switch (mmd->transition) {
353  /* Show in forward order
354  * - As fac increases, the number of visible points increases
355  */
357  first_visible = 0; /* always visible */
358  last_visible = (size_t)roundf(totpoints * use_fac);
359  fade_start = (int)roundf(totpoints * use_fade_fac);
360  fade_end = last_visible;
361  break;
362 
363  /* Hide in reverse order
364  * - As fac increases, the number of points visible at the end decreases
365  */
367  first_visible = 0; /* always visible (until last point removed) */
368  last_visible = (size_t)(totpoints * (1.0f + set_fade_fac - use_fac));
369  fade_start = (int)roundf(totpoints * (1.0f - use_fade_fac - set_fade_fac));
370  fade_end = last_visible;
371  break;
372 
373  /* Hide in forward order
374  * - As fac increases, the early points start getting hidden
375  */
377  first_visible = (size_t)(totpoints * use_fade_fac);
378  last_visible = totpoints; /* i.e. visible until the end, unless first overlaps this */
379  fade_start = first_visible;
380  fade_end = (int)roundf(totpoints * use_fac);
381  break;
382  }
383 
384  /* 4) Go through all strokes, deciding which to keep, and/or how much of each to keep */
385  for (i = 0; i < tot_strokes; i++) {
386  tStrokeBuildDetails *cell = &table[i];
387 
388  /* Determine what portion of the stroke is visible */
389  if ((cell->end_idx < first_visible) || (cell->start_idx > last_visible)) {
390  /* Not visible at all - Either ended before */
391  clear_stroke(gpf, cell->gps);
392  }
393  else {
394  if (fade_start != fade_end && (int)cell->start_idx < fade_end &&
395  (int)cell->end_idx > fade_start) {
396  int start_index = fade_start - cell->start_idx;
397  int end_index = cell->totpoints + fade_end - cell->end_idx - 1;
398  CLAMP(start_index, 0, cell->totpoints - 1);
399  CLAMP(end_index, 0, cell->totpoints - 1);
400  float start_weight = ratiof(fade_start, fade_end, cell->start_idx + start_index);
401  float end_weight = ratiof(fade_start, fade_end, cell->start_idx + end_index);
403  start_weight = 1.0f - start_weight;
404  end_weight = 1.0f - end_weight;
405  }
406  fade_stroke_points(cell->gps,
407  start_index,
408  end_index,
409  start_weight,
410  end_weight,
411  target_def_nr,
412  mmd->transition,
414  mmd->fade_opacity_strength);
415  /* Calc geometry data. */
417  }
418  /* Some proportion of stroke is visible */
419  if ((first_visible <= cell->start_idx) && (last_visible >= cell->end_idx)) {
420  /* Do nothing - whole stroke is visible */
421  }
422  else if (first_visible > cell->start_idx) {
423  /* Starts partway through this stroke */
424  int points_num = cell->end_idx - first_visible;
425  reduce_stroke_points(gpd, gpf, cell->gps, points_num, mmd->transition);
426  }
427  else {
428  /* Ends partway through this stroke */
429  int points_num = last_visible - cell->start_idx;
430  reduce_stroke_points(gpd, gpf, cell->gps, points_num, mmd->transition);
431  }
432  }
433  }
434 
435  /* Free table */
436  MEM_freeN(table);
437 }
438 
439 /* --------------------------------------------- */
440 
441 /* Concurrent - Show multiple strokes at once */
443  bGPdata *gpd,
444  bGPDframe *gpf,
445  const int target_def_nr,
446  float fac)
447 {
448  bGPDstroke *gps, *gps_next;
449  int max_points = 0;
450 
451  const bool reverse = (mmd->transition != GP_BUILD_TRANSITION_GROW);
452 
453  /* 1) Determine the longest stroke, to figure out when short strokes should start */
454  /* FIXME: A *really* long stroke here could dwarf everything else, causing bad timings */
455  for (gps = gpf->strokes.first; gps; gps = gps->next) {
456  if (gps->totpoints > max_points) {
457  max_points = gps->totpoints;
458  }
459  }
460  if (max_points == 0) {
461  printf("ERROR: Strokes are all empty (GP Build Modifier: %s)\n", __func__);
462  return;
463  }
464 
465  bool fading_enabled = (mmd->flag & GP_BUILD_USE_FADING);
466  float set_fade_fac = fading_enabled ? mmd->fade_fac : 0.0f;
467  float use_fac = interpf(1 + set_fade_fac, 0, fac);
468  use_fac = reverse ? use_fac - set_fade_fac : use_fac;
469  int fade_points = set_fade_fac * max_points;
470 
471  /* 2) For each stroke, determine how it should be handled */
472  for (gps = gpf->strokes.first; gps; gps = gps_next) {
473  gps_next = gps->next;
474 
475  /* Relative Length of Stroke - Relative to the longest stroke,
476  * what proportion of the available time should this stroke use
477  */
478  const float relative_len = (float)gps->totpoints / (float)max_points;
479 
480  /* Determine how many points should be left in the stroke */
481  int points_num = 0;
482 
483  switch (mmd->time_alignment) {
484  case GP_BUILD_TIMEALIGN_START: /* all start on frame 1 */
485  {
486  /* Scale fac to fit relative_len */
487  const float scaled_fac = use_fac / MAX2(relative_len, PSEUDOINVERSE_EPSILON);
488 
489  if (reverse) {
490  points_num = (int)roundf((1.0f - scaled_fac) * gps->totpoints);
491  }
492  else {
493  points_num = (int)roundf(scaled_fac * gps->totpoints);
494  }
495 
496  break;
497  }
498  case GP_BUILD_TIMEALIGN_END: /* all end on same frame */
499  {
500  /* Build effect occurs over 1.0 - relative_len, to 1.0 (i.e. over the end of the range)
501  */
502  const float start_fac = 1.0f - relative_len;
503 
504  const float scaled_fac = (use_fac - start_fac) / MAX2(relative_len, PSEUDOINVERSE_EPSILON);
505 
506  if (reverse) {
507  points_num = (int)roundf((1.0f - scaled_fac) * gps->totpoints);
508  }
509  else {
510  points_num = (int)roundf(scaled_fac * gps->totpoints);
511  }
512 
513  break;
514  }
515  }
516 
517  /* Modify the stroke geometry */
518  if (points_num <= 0) {
519  /* Nothing Left - Delete the stroke */
520  clear_stroke(gpf, gps);
521  }
522  else {
523  int more_points = points_num - gps->totpoints;
524  CLAMP(more_points, 0, fade_points + 1);
525  float max_weight = (float)(points_num + more_points) / fade_points;
526  CLAMP(max_weight, 0.0f, 1.0f);
527  int starting_index = mmd->transition == GP_BUILD_TRANSITION_VANISH ?
528  gps->totpoints - points_num - more_points :
529  points_num - 1 - fade_points + more_points;
530  int ending_index = mmd->transition == GP_BUILD_TRANSITION_VANISH ?
531  gps->totpoints - points_num + fade_points - more_points :
532  points_num - 1 + more_points;
533  float starting_weight = mmd->transition == GP_BUILD_TRANSITION_VANISH ?
534  ((float)more_points / fade_points) :
535  max_weight;
536  float ending_weight = mmd->transition == GP_BUILD_TRANSITION_VANISH ?
537  max_weight :
538  ((float)more_points / fade_points);
539  CLAMP(starting_index, 0, gps->totpoints - 1);
540  CLAMP(ending_index, 0, gps->totpoints - 1);
541  fade_stroke_points(gps,
542  starting_index,
543  ending_index,
544  starting_weight,
545  ending_weight,
546  target_def_nr,
547  mmd->transition,
549  mmd->fade_opacity_strength);
550  if (points_num < gps->totpoints) {
551  /* Remove some points */
552  reduce_stroke_points(gpd, gpf, gps, points_num, mmd->transition);
553  }
554  }
555  }
556 }
557 
558 /* --------------------------------------------- */
559 
562  Object *ob,
563  bGPdata *gpd,
564  bGPDlayer *gpl,
565  bGPDframe *gpf)
566 {
568  if (mmd->mode == GP_BUILD_MODE_ADDITIVE) {
570  }
571  const bool reverse = (mmd->transition != GP_BUILD_TRANSITION_GROW);
572  const bool is_percentage = (mmd->flag & GP_BUILD_PERCENTAGE);
573 
574  const float ctime = DEG_get_ctime(depsgraph);
575 
576  /* Early exit if it's an empty frame */
577  if (gpf->strokes.first == NULL) {
578  return;
579  }
580 
581  /* Omit layer if filter by layer */
582  if (mmd->layername[0] != '\0') {
583  if ((mmd->flag & GP_BUILD_INVERT_LAYER) == 0) {
584  if (!STREQ(mmd->layername, gpl->info)) {
585  return;
586  }
587  }
588  else {
589  if (STREQ(mmd->layername, gpl->info)) {
590  return;
591  }
592  }
593  }
594  /* verify layer pass */
595  if (mmd->layer_pass > 0) {
596  if ((mmd->flag & GP_BUILD_INVERT_LAYERPASS) == 0) {
597  if (gpl->pass_index != mmd->layer_pass) {
598  return;
599  }
600  }
601  else {
602  if (gpl->pass_index == mmd->layer_pass) {
603  return;
604  }
605  }
606  }
607 
608  int target_def_nr = -1;
609  if (mmd->flag & GP_BUILD_USE_FADING) {
610  /* If there are weight output, initialize it with a default weight of 1. */
611  target_def_nr = BKE_object_defgroup_name_index(ob, mmd->target_vgname);
612  if (target_def_nr >= 0) {
613  LISTBASE_FOREACH (bGPDstroke *, fgps, &gpf->strokes) {
615  /* Assign a initial weight of 1, and only process those who needs additional fading. */
616  for (int j = 0; j < fgps->totpoints; j++) {
617  MDeformVert *dvert = &fgps->dvert[j];
618  MDeformWeight *dw = BKE_defvert_ensure_index(dvert, target_def_nr);
619  if (dw) {
620  dw->weight = 1.0f;
621  }
622  }
623  }
624  }
625  }
626 
627  /* Early exit if outside of the frame range for this modifier
628  * (e.g. to have one forward, and one backwards modifier)
629  */
630  if (mmd->flag & GP_BUILD_RESTRICT_TIME) {
631  if ((ctime < mmd->start_frame) || (ctime > mmd->end_frame)) {
632  return;
633  }
634  }
635 
636  /* Compute start and end frames for the animation effect
637  * By default, the upper bound is given by the "maximum length" setting
638  */
639  float start_frame = is_percentage ? gpf->framenum : gpf->framenum + mmd->start_delay;
640  /* When use percentage don't need a limit in the upper bound, so use a maximum value for the last
641  * frame. */
642  float end_frame = is_percentage ? start_frame + 9999 : start_frame + mmd->length;
643 
644  if (gpf->next) {
645  /* Use the next frame or upper bound as end frame, whichever is lower/closer */
646  end_frame = MIN2(end_frame, gpf->next->framenum);
647  }
648 
649  /* Early exit if current frame is outside start/end bounds */
650  /* NOTE: If we're beyond the next/previous frames (if existent),
651  * then we wouldn't have this problem anyway... */
652  if (ctime < start_frame) {
653  /* Before Start - Animation hasn't started. Display initial state. */
654  if (reverse) {
655  /* 1) Reverse = Start with all, end with nothing.
656  * ==> Do nothing (everything already present)
657  */
658  }
659  else {
660  /* 2) Forward Order = Start with nothing, end with the full frame.
661  * ==> Free all strokes, and return an empty frame
662  */
664  }
665 
666  /* Early exit */
667  return;
668  }
669  if (ctime >= end_frame) {
670  /* Past End - Animation finished. Display final result. */
671  if (reverse) {
672  /* 1) Reverse = Start with all, end with nothing.
673  * ==> Free all strokes, and return an empty frame
674  */
676  }
677  else {
678  /* 2) Forward Order = Start with nothing, end with the full frame.
679  * ==> Do Nothing (everything already present)
680  */
681  }
682 
683  /* Early exit */
684  return;
685  }
686 
687  /* Determine how far along we are between the keyframes */
688  float fac = is_percentage ? mmd->percentage_fac :
689  (ctime - start_frame) / (end_frame - start_frame);
690 
691  /* Time management mode */
692  switch (mmd->mode) {
694  build_sequential(ob, mmd, gpd, gpf, target_def_nr, fac, false);
695  break;
696 
698  build_concurrent(mmd, gpd, gpf, target_def_nr, fac);
699  break;
700 
702  build_sequential(ob, mmd, gpd, gpf, target_def_nr, fac, true);
703  break;
704 
705  default:
706  printf("Unsupported build mode (%d) for GP Build Modifier: '%s'\n",
707  mmd->mode,
708  mmd->modifier.name);
709  break;
710  }
711 }
712 
713 /* Entry-point for Build Modifier */
715 {
717  bGPdata *gpd = (bGPdata *)ob->data;
718 
719  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
721  if (gpf == NULL) {
722  continue;
723  }
724  generate_geometry(md, depsgraph, ob, gpd, gpl, gpf);
725  }
726 }
727 
728 static void panel_draw(const bContext *UNUSED(C), Panel *panel)
729 {
730  uiLayout *row, *sub;
731  uiLayout *layout = panel->layout;
732 
733  PointerRNA ob_ptr;
735 
736  int mode = RNA_enum_get(ptr, "mode");
737  const bool use_percentage = RNA_boolean_get(ptr, "use_percentage");
738 
739  uiLayoutSetPropSep(layout, true);
740 
741  uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE);
742  if (mode == GP_BUILD_MODE_CONCURRENT) {
743  uiItemR(layout, ptr, "concurrent_time_alignment", 0, NULL, ICON_NONE);
744  }
745 
746  uiItemS(layout);
747 
749  uiItemR(layout, ptr, "transition", 0, NULL, ICON_NONE);
750  }
751  row = uiLayoutRow(layout, true);
752  uiLayoutSetActive(row, !use_percentage);
753  uiItemR(row, ptr, "start_delay", 0, NULL, ICON_NONE);
754  row = uiLayoutRow(layout, true);
755  uiLayoutSetActive(row, !use_percentage);
756  uiItemR(row, ptr, "length", 0, IFACE_("Frames"), ICON_NONE);
757 
758  uiItemS(layout);
759 
760  row = uiLayoutRowWithHeading(layout, true, IFACE_("Factor"));
761  uiLayoutSetPropDecorate(row, false);
762  uiItemR(row, ptr, "use_percentage", 0, "", ICON_NONE);
763  sub = uiLayoutRow(row, true);
764  uiLayoutSetActive(sub, use_percentage);
765  uiItemR(sub, ptr, "percentage_factor", 0, "", ICON_NONE);
766  uiItemDecoratorR(row, ptr, "percentage_factor", 0);
767 
768  uiItemS(layout);
769 
771  uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE);
772  }
773 
774  /* Check for incompatible time modifier. */
775  Object *ob = ob_ptr.data;
778  BKE_gpencil_modifier_set_error(md, "Build and Time Offset modifiers are incompatible");
779  }
780 
782 }
783 
784 static void frame_range_header_draw(const bContext *UNUSED(C), Panel *panel)
785 {
786  uiLayout *layout = panel->layout;
787 
789 
790  uiItemR(layout, ptr, "use_restrict_frame_range", 0, IFACE_("Custom Range"), ICON_NONE);
791 }
792 
793 static void frame_range_panel_draw(const bContext *UNUSED(C), Panel *panel)
794 {
795  uiLayout *col;
796  uiLayout *layout = panel->layout;
797 
799 
800  uiLayoutSetPropSep(layout, true);
801 
802  col = uiLayoutColumn(layout, false);
803  uiItemR(col, ptr, "frame_start", 0, IFACE_("Start"), ICON_NONE);
804  uiItemR(col, ptr, "frame_end", 0, IFACE_("End"), ICON_NONE);
805 }
806 
807 static void fading_header_draw(const bContext *UNUSED(C), Panel *panel)
808 {
809  uiLayout *layout = panel->layout;
810 
812 
813  uiItemR(layout, ptr, "use_fading", 0, IFACE_("Fade"), ICON_NONE);
814 }
815 
816 static void fading_panel_draw(const bContext *UNUSED(C), Panel *panel)
817 {
818  uiLayout *col;
819  uiLayout *layout = panel->layout;
820 
821  PointerRNA ob_ptr;
823 
824  uiLayoutSetPropSep(layout, true);
825 
826  uiItemR(layout, ptr, "fade_factor", 0, IFACE_("Factor"), ICON_NONE);
827 
828  col = uiLayoutColumn(layout, true);
829  uiItemR(col, ptr, "fade_thickness_strength", 0, IFACE_("Thickness"), ICON_NONE);
830  uiItemR(col, ptr, "fade_opacity_strength", 0, IFACE_("Opacity"), ICON_NONE);
831 
832  uiItemPointerR(layout,
833  ptr,
834  "target_vertex_group",
835  &ob_ptr,
836  "vertex_groups",
837  IFACE_("Weight Output"),
838  ICON_NONE);
839 }
840 
841 static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
842 {
843  gpencil_modifier_masking_panel_draw(panel, false, false);
844 }
845 
846 static void panelRegister(ARegionType *region_type)
847 {
849  region_type, eGpencilModifierType_Build, panel_draw);
851  region_type, "frame_range", "", frame_range_header_draw, frame_range_panel_draw, panel_type);
853  region_type, "fading", "", fading_header_draw, fading_panel_draw, panel_type);
855  region_type, "_mask", "Influence", NULL, mask_panel_draw, panel_type);
856 }
857 
858 static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
859 {
861 
862  walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP);
863 }
864 
867  const int UNUSED(mode))
868 {
870  if (lmd->object != NULL) {
871  DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Build Modifier");
872  DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Build Modifier");
873  }
874  DEG_add_object_relation(ctx->node, ctx->object, DEG_OB_COMP_TRANSFORM, "Build Modifier");
875 }
876 
877 /* ******************************************** */
878 
880  /* name */ N_("Build"),
881  /* structName */ "BuildGpencilModifierData",
882  /* structSize */ sizeof(BuildGpencilModifierData),
885 
886  /* copyData */ copyData,
887 
888  /* deformStroke */ NULL,
889  /* generateStrokes */ generateStrokes,
890  /* bakeModifier */ NULL,
891  /* remapTime */ NULL,
892 
893  /* initData */ initData,
894  /* freeData */ NULL,
895  /* isDisabled */ NULL,
896  /* updateDepsgraph */ updateDepsgraph,
897  /* dependsOnTime */ dependsOnTime,
898  /* foreachIDLink */ foreachIDLink,
899  /* foreachTexLink */ NULL,
900  /* panelRegister */ panelRegister,
901 };
typedef float(TangentPoint)[2]
support for deformation groups and hooks.
int BKE_object_defgroup_name_index(const struct Object *ob, const char *name)
struct MDeformWeight * BKE_defvert_ensure_index(struct MDeformVert *dv, int defgroup)
Definition: deform.c:748
void BKE_gpencil_free_point_weights(struct MDeformVert *dvert)
Definition: gpencil.c:353
void BKE_gpencil_dvert_ensure(struct bGPDstroke *gps)
Definition: gpencil.c:1891
void BKE_gpencil_free_stroke(struct bGPDstroke *gps)
Definition: gpencil.c:391
void BKE_gpencil_stroke_flip(struct bGPDstroke *gps)
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)
void BKE_gpencil_modifier_set_error(struct GpencilModifierData *md, const char *format,...) ATTR_PRINTF_FORMAT(2
@ eGpencilModifierTypeFlag_NoApply
struct bGPDframe * BKE_gpencil_frame_retime_get(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, struct bGPDlayer *gpl)
@ eGpencilModifierTypeType_Gpencil
struct GpencilModifierData * BKE_gpencil_modifiers_findby_type(struct Object *ob, GpencilModifierType type)
@ IDWALK_CB_NOP
Definition: BKE_lib_query.h:33
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
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:273
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float interpf(float a, float b, float t)
MINLINE float ratiof(float min, float max, float pos)
#define PSEUDOINVERSE_EPSILON
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:739
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
#define UNUSED(x)
#define MAX2(a, b)
#define ELEM(...)
#define MIN2(a, b)
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define MEMCPY_STRUCT_AFTER(struct_dst, struct_src, member)
#define STREQ(a, b)
#define IFACE_(msgid)
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
void DEG_add_object_relation(struct DepsNodeHandle *node_handle, struct Object *object, eDepsObjectComponentType component, const char *description)
@ DEG_OB_COMP_GEOMETRY
@ DEG_OB_COMP_TRANSFORM
float DEG_get_ctime(const Depsgraph *graph)
struct Scene * DEG_get_evaluated_scene(const struct Depsgraph *graph)
#define DNA_struct_default_get(struct_name)
Definition: DNA_defaults.h:29
struct BuildGpencilModifierData BuildGpencilModifierData
@ GP_BUILD_TRANSITION_SHRINK
@ GP_BUILD_TRANSITION_VANISH
@ GP_BUILD_TRANSITION_GROW
@ GP_BUILD_MODE_SEQUENTIAL
@ GP_BUILD_MODE_ADDITIVE
@ GP_BUILD_MODE_CONCURRENT
@ GP_BUILD_TIMEALIGN_START
@ GP_BUILD_TIMEALIGN_END
@ GP_BUILD_RESTRICT_TIME
@ GP_BUILD_INVERT_LAYERPASS
@ eGpencilModifierType_Time
@ eGpencilModifierType_Build
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)
static void fading_header_draw(const bContext *UNUSED(C), Panel *panel)
static void frame_range_header_draw(const bContext *UNUSED(C), Panel *panel)
static bool dependsOnTime(GpencilModifierData *UNUSED(md))
static void fading_panel_draw(const bContext *UNUSED(C), Panel *panel)
static int cmp_stroke_build_details(const void *ps1, const void *ps2)
static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx, const int UNUSED(mode))
static void frame_range_panel_draw(const bContext *UNUSED(C), Panel *panel)
struct tStrokeBuildDetails tStrokeBuildDetails
static void fade_stroke_points(bGPDstroke *gps, const int starting_index, const int ending_index, const float starting_weight, const float ending_weight, const int target_def_nr, const eBuildGpencil_Transition transition, const float thickness_strength, const float opacity_strength)
static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel)
static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
static void build_concurrent(BuildGpencilModifierData *mmd, bGPdata *gpd, bGPDframe *gpf, const int target_def_nr, float fac)
static void build_sequential(Object *ob, BuildGpencilModifierData *mmd, bGPdata *gpd, bGPDframe *gpf, const int target_def_nr, float fac, bool additive)
static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
static void clear_stroke(bGPDframe *gpf, bGPDstroke *gps)
static void panelRegister(ARegionType *region_type)
static void reduce_stroke_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, const int points_num, const eBuildGpencil_Transition transition)
static void gpf_clear_all_strokes(bGPDframe *gpf)
static void initData(GpencilModifierData *md)
static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob)
static void generate_geometry(GpencilModifierData *md, Depsgraph *depsgraph, Object *ob, bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf)
GpencilModifierTypeInfo modifierType_Gpencil_Build
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
#define C
Definition: RandGen.cpp:25
uiLayout * uiLayoutRowWithHeading(uiLayout *layout, bool align, const char *heading)
void uiLayoutSetActive(uiLayout *layout, bool active)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
void uiItemS(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiItemDecoratorR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int index)
void uiItemPointerR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, struct PointerRNA *searchptr, const char *searchpropname, const char *name, int icon)
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
Scene scene
const Depsgraph * depsgraph
uint col
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
Definition: DNA_ID.h:368
void * first
Definition: DNA_listBase.h:31
struct DepsNodeHandle * node
Definition: BKE_modifier.h:134
float loc[3]
float obmat[4][4]
void * data
struct uiLayout * layout
void * data
Definition: RNA_types.h:38
struct bGPDframe * next
ListBase strokes
struct bGPDframe * prev
char info[128]
bGPDspoint * points
struct MDeformVert * dvert
struct bGPDstroke * next
ListBase layers
#define N_(msgid)
PointerRNA * ptr
Definition: wm_files.c:3480