[Bf-blender-cvs] [250eda36b8f] master: GPencil: Build modifier add "natural drawing" time

Marc Chehab noreply at git.blender.org
Tue Jan 10 15:38:58 CET 2023


Commit: 250eda36b8f91a2f89b202d5eb79108260e1b1e3
Author: Marc Chehab
Date:   Tue Jan 10 15:37:02 2023 +0100
Branches: master
https://developer.blender.org/rB250eda36b8f91a2f89b202d5eb79108260e1b1e3

GPencil: Build modifier add "natural drawing" time

This patch uses the recorded drawing speed to rebuild the strokes. This results in a way more
natural feel of the animation.

Here's a short summary of existing data used:

- gps->points->time: This is a timestamp in seconds of when the point was created
  since the creation of the stroke. It's quite often 0 (I added a sanitization routine).
- gpf->inittime: This is a timestamp in seconds when a stroke was drawn measured
  since some unknown point in time. I only ever use the difference between two strokes,
  so the absolute value is not relevant.

Reviewed By: frogstomp, antoniov, mendio

Differential Revision: https://developer.blender.org/D16759

===================================================================

M	source/blender/blenloader/intern/versioning_300.cc
M	source/blender/editors/include/ED_gpencil.h
M	source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
M	source/blender/makesdna/DNA_gpencil_modifier_defaults.h
M	source/blender/makesdna/DNA_gpencil_modifier_types.h
M	source/blender/makesrna/intern/rna_gpencil_modifier.c

===================================================================

diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc
index 38ecfaf41ea..6b4f374bf0e 100644
--- a/source/blender/blenloader/intern/versioning_300.cc
+++ b/source/blender/blenloader/intern/versioning_300.cc
@@ -3842,6 +3842,20 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain)
     LISTBASE_FOREACH (Light *, light, &bmain->lights) {
       light->radius = light->area_size;
     }
+    /* Grease Pencil Build modifier: Set default value for new natural drawspeed factor and maximum
+     * gap. */
+    if (!DNA_struct_elem_find(fd->filesdna, "BuildGpencilModifierData", "float", "speed_fac") ||
+        !DNA_struct_elem_find(fd->filesdna, "BuildGpencilModifierData", "float", "speed_maxgap")) {
+      LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+        LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) {
+          if (md->type == eGpencilModifierType_Build) {
+            BuildGpencilModifierData *mmd = (BuildGpencilModifierData *)md;
+            mmd->speed_fac = 1.2f;
+            mmd->speed_maxgap = 0.5f;
+          }
+        }
+      }
+    }
   }
 
   /**
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index 9dd2ba5d1d3..c95e58f9559 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -83,7 +83,7 @@ typedef struct tGPspoint {
   float pressure;
   /** Pressure of tablet at this point for alpha factor. */
   float strength;
-  /** Time relative to stroke start (used when converting to path). */
+  /** Time relative to stroke start (used when converting to path & in build modifier). */
   float time;
   /** Factor of uv along the stroke. */
   float uv_fac;
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
index 49ac3275c82..e4320a7b5a8 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
@@ -47,6 +47,13 @@
 #include "MOD_gpencil_modifiertypes.h"
 #include "MOD_gpencil_ui_common.h"
 
+/* Two hard-coded values for GP_BUILD_MODE_ADDITIVE with GP_BUILD_TIMEMODE_DRAWSPEED. */
+
+/* The minimum time gap we should worry about points with no time. */
+#define GP_BUILD_CORRECTGAP 0.001
+/* The time for geometric strokes */
+#define GP_BUILD_TIME_GEOSTROKES 1.0
+
 static void initData(GpencilModifierData *md)
 {
   BuildGpencilModifierData *gpmd = (BuildGpencilModifierData *)md;
@@ -252,53 +259,55 @@ static int cmp_stroke_build_details(const void *ps1, const void *ps2)
   return p1->distance > p2->distance ? 1 : (p1->distance == p2->distance ? 0 : -1);
 }
 
-/* Sequential and additive - Show strokes one after the other. */
+/* Sequential - Show strokes one after the other (includes additive mode). */
 static void build_sequential(Object *ob,
                              BuildGpencilModifierData *mmd,
+                             Depsgraph *depsgraph,
                              bGPdata *gpd,
                              bGPDframe *gpf,
-                             const int target_def_nr,
+                             int target_def_nr,
                              float fac,
-                             bool additive)
+                             const float *ctime)
 {
+  /* Total number of strokes in this run. */
   size_t tot_strokes = BLI_listbase_count(&gpf->strokes);
-  size_t start_stroke;
+  /* First stroke to build. */
+  size_t start_stroke = 0;
+  /* Pointer to current stroke. */
   bGPDstroke *gps;
+  /* Recycled counter. */
   size_t i;
+  Scene *scene = DEG_get_evaluated_scene(depsgraph);
+  /* Framerate of scene. */
+  const float fps = (((float)scene->r.frs_sec) / scene->r.frs_sec_base);
 
-  /* 1) Determine which strokes to start with & total strokes to build. */
-
-  if (additive) {
+  /* 1) Determine which strokes to start with (& adapt total number of strokes to build). */
+  if (mmd->mode == GP_BUILD_MODE_ADDITIVE) {
     if (gpf->prev) {
-      start_stroke = BLI_listbase_count(&gpf->prev->strokes);
-    }
-    else {
-      start_stroke = 0;
+      start_stroke = BLI_listbase_count(&gpf->runtime.gpf_orig->prev->strokes);
     }
     if (start_stroke <= tot_strokes) {
       tot_strokes = tot_strokes - start_stroke;
     }
-    else {
-      start_stroke = 0;
-    }
-  }
-  else {
-    start_stroke = 0;
   }
 
-  /* 2) Compute proportion of time each stroke should occupy */
+  /* 2) Compute proportion of time each stroke should occupy. */
   /* NOTE: This assumes that the total number of points won't overflow! */
   tStrokeBuildDetails *table = MEM_callocN(sizeof(tStrokeBuildDetails) * tot_strokes, __func__);
-  size_t totpoints = 0;
-
-  /* 2.1) First pass - Tally up points */
+  /* Pointer to cache table of times for each point. */
+  float *idx_times;
+  /* Running overall time sum incrementing per point. */
+  float sumtime = 0;
+  /* Running overall point sum. */
+  size_t sumpoints = 0;
+
+  /* 2.1) Pass to initially tally up points. */
   for (gps = BLI_findlink(&gpf->strokes, start_stroke), i = 0; gps; gps = gps->next, i++) {
     tStrokeBuildDetails *cell = &table[i];
 
     cell->gps = gps;
     cell->totpoints = gps->totpoints;
-
-    totpoints += cell->totpoints;
+    sumpoints += cell->totpoints;
 
     /* Compute distance to control object if set, and build according to that order. */
     if (mmd->object) {
@@ -321,7 +330,104 @@ static void build_sequential(Object *ob,
     qsort(table, tot_strokes, sizeof(tStrokeBuildDetails), cmp_stroke_build_details);
   }
 
-  /* 2.2) Second pass - Compute the overall indices for points */
+  /* 2.2) If GP_BUILD_TIMEMODE_DRAWSPEED: Tally up point timestamps & delays to idx_times. */
+  if (mmd->time_mode == GP_BUILD_TIMEMODE_DRAWSPEED) {
+    idx_times = MEM_callocN(sizeof(float) * sumpoints, __func__);
+    /* Maximum time gap between strokes in seconds. */
+    const float GP_BUILD_MAXGAP = mmd->speed_maxgap;
+    /* Running reference to overall current point. */
+    size_t curpoint = 0;
+    /* Running timestamp of last point that had data. */
+    float last_pointtime = 0;
+
+    for (i = 0; i < tot_strokes; i++) {
+      tStrokeBuildDetails *cell = &table[i];
+      /* Adding delay between strokes to sumtime. */
+      if (mmd->object == NULL) {
+        /* Normal case: Delay to last stroke. */
+        if (i != 0 && 0 < cell->gps->inittime && 0 < (cell - 1)->gps->inittime) {
+          float curgps_delay = fabs(cell->gps->inittime - (cell - 1)->gps->inittime) -
+                               last_pointtime;
+          if (0 < curgps_delay) {
+            sumtime += MIN2(curgps_delay, GP_BUILD_MAXGAP);
+          }
+        }
+      }
+
+      /* Going through the points of the current stroke
+       * and filling in "zeropoints" where "time" = 0. */
+
+      /* Count of consecutive points where "time" is 0. */
+      int zeropoints = 0;
+      for (int j = 0; j < cell->totpoints; j++) {
+        /* Defining time for first point in stroke. */
+        if (j == 0) {
+          idx_times[curpoint] = sumtime;
+          last_pointtime = cell->gps->points[0].time;
+        }
+        /* Entering subsequent points */
+        else {
+          if (cell->gps->points[j].time == 0) {
+            idx_times[curpoint] = sumtime;
+            zeropoints++;
+          }
+          /* From here current point has time data */
+          else {
+            float deltatime = fabs(cell->gps->points[j].time - last_pointtime);
+            /* Do we need to sanitize previous points? */
+            if (0 < zeropoints) {
+              /* Only correct if timegap bigger than MIN_CORRECTGAP. */
+              if (GP_BUILD_CORRECTGAP < deltatime) {
+                /* Cycling backwards through zeropoints to fix them. */
+                for (int k = 0; k < zeropoints; k++) {
+                  float linear_fill = interpf(
+                      deltatime, 0, ((float)k + 1) / (zeropoints + 1)); /* Factor = Proportion. */
+                  idx_times[curpoint - k - 1] = sumtime + linear_fill;
+                }
+              }
+              else {
+                zeropoints = 0;
+              }
+            }
+
+            /* Normal behaviour with time data */
+            idx_times[curpoint] = sumtime + deltatime;
+            sumtime = idx_times[curpoint];
+            last_pointtime = cell->gps->points[j].time;
+            zeropoints = 0;
+          }
+        }
+        curpoint += 1;
+      }
+
+      /* If stroke had no time data at all, use mmd->time_geostrokes. */
+      if (zeropoints + 1 == cell->totpoints) {
+        for (int j = 0; j < cell->totpoints; j++) {
+          idx_times[(int)curpoint - j - 1] = (float)(cell->totpoints - j) *
+                                                 GP_BUILD_TIME_GEOSTROKES /
+                                                 (float)cell->totpoints +
+                                             sumtime;
+        }
+        last_pointtime = GP_BUILD_TIME_GEOSTROKES;
+        sumtime += GP_BUILD_TIME_GEOSTROKES;
+      }
+    }
+
+    float gp_build_speedfactor = mmd->speed_fac;
+    /* If current frame can't be built before next frame, adjust gp_build_speedfactor. */
+    if (gpf->next &&
+        (gpf->framenum + sumtime * fps / gp_build_speedfactor) > gpf->next->framenum) {
+      gp_build_speedfactor = sumtime * fps / (gpf->next->framenum - gpf->framenum);
+    }
+    /* Apply gp_build_speedfactor to all points & to sumtime. */
+    for (i = 0; i < sumpoints; i++) {
+      float *idx_time = &idx_times[i];
+      *idx_time /= gp_build_speedfactor;
+    }
+    sumtime /= gp_build_speedfactor;
+  }
+
+  /* 2.3) Pass to compute overall indices for points (per stroke). */
   for (i = 0; i < tot_strokes; i++) {
     tStrokeBuildDetails *cell = &table[i];
 
@@ -329,12 +435,12 @@ static void build_sequential(Object *ob,
       cell->start_idx = 0;
     }
     else {
-      cell->start_idx = (cell - 1)->end_idx;
+      cell->start_idx = (cell - 1)->end_idx + 1;
     }
     cell->end_idx = cell->start_idx + cell->totpoints - 1;
   }
 
-  /* 3) Determine the

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list