ba814c8538ac3bb06828add60775412c9ef69a9f
[blender.git] / source / blender / gpencil_modifiers / intern / MOD_gpencilbuild.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software  Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2017, Blender Foundation
19  * This is a new part of Blender
20  *
21  * Contributor(s): Antonio Vazquez, Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  *
25  */
26
27 /** \file blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
28  *  \ingroup modifiers
29  */
30
31 #include <stdio.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "DNA_meshdata_types.h"
36 #include "DNA_scene_types.h"
37 #include "DNA_object_types.h"
38 #include "DNA_gpencil_types.h"
39 #include "DNA_gpencil_modifier_types.h"
40
41 #include "BLI_blenlib.h"
42 #include "BLI_math.h"
43 #include "BLI_utildefines.h"
44
45 #include "BKE_context.h"
46 #include "BKE_gpencil.h"
47 #include "BKE_gpencil_modifier.h"
48
49 #include "DEG_depsgraph.h"
50 #include "DEG_depsgraph_query.h"
51
52 #include "MOD_gpencil_util.h"
53 #include "MOD_gpencil_modifiertypes.h"
54
55 static void initData(GpencilModifierData *md)
56 {
57         BuildGpencilModifierData *gpmd = (BuildGpencilModifierData *)md;
58
59         /* We deliberately set this range to the half the default
60          * frame-range to have an immediate effect ot suggest use-cases
61          */
62         gpmd->start_frame = 1;
63         gpmd->end_frame = 125;
64
65         /* Init default length of each build effect - Nothing special */
66         gpmd->start_delay = 0.0f;
67         gpmd->length = 100.0f;
68 }
69
70 static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
71 {
72         BKE_gpencil_modifier_copyData_generic(md, target);
73 }
74
75 static bool dependsOnTime(GpencilModifierData *UNUSED(md))
76 {
77         return true;
78 }
79
80 /* ******************************************** */
81 /* Build Modifier - Stroke generation logic
82  *
83  * There are two modes for how the strokes are sequenced (at a macro-level):
84  * - Sequential Mode - Strokes appear/disappear one after the other. Only a single one changes at a time.
85  * - Concurrent Mode - Multiple strokes appear/disappear at once.
86  *
87  * Assumptions:
88  * - Stroke points are generally equally spaced. This implies that we can just add/remove points,
89  *   without worrying about distances between them / adding extra interpolated points between
90  *   an visible point and one about to be added/removed (or any similar tapering effects).
91
92  * - All strokes present are fully visible (i.e. we don't have to ignore any)
93  */
94
95 /* Remove a particular stroke */
96 static void clear_stroke(bGPDframe *gpf, bGPDstroke *gps)
97 {
98         BLI_remlink(&gpf->strokes, gps);
99         BKE_gpencil_free_stroke(gps);
100 }
101
102 /* Clear all strokes in frame */
103 static void gpf_clear_all_strokes(bGPDframe *gpf)
104 {
105         bGPDstroke *gps, *gps_next;
106         for (gps = gpf->strokes.first; gps; gps = gps_next) {
107                 gps_next = gps->next;
108                 clear_stroke(gpf, gps);
109         }
110         BLI_listbase_clear(&gpf->strokes);
111 }
112
113 /* Reduce the number of points in the stroke
114  *
115  * Note: This won't be called if all points are present/removed
116  * TODO: Allow blending of growing/shrinking tip (e.g. for more gradual transitions)
117  */
118 static void reduce_stroke_points(bGPDstroke *gps, const int num_points, const eBuildGpencil_Transition transition)
119 {
120         bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * num_points, __func__);
121         MDeformVert *new_dvert = NULL;
122         if (gps->dvert != NULL) {
123                 new_dvert = MEM_callocN(sizeof(MDeformVert) * num_points, __func__);
124         }
125
126         /* Which end should points be removed from */
127         // TODO: free stroke weights
128         switch (transition) {
129                 case GP_BUILD_TRANSITION_GROW:   /* Show in forward order = Remove ungrown-points from end of stroke */
130                 case GP_BUILD_TRANSITION_SHRINK: /* Hide in reverse order = Remove dead-points from end of stroke */
131                 {
132                         /* copy over point data */
133                         memcpy(new_points, gps->points, sizeof(bGPDspoint) * num_points);
134                         if (gps->dvert != NULL) {
135                                 memcpy(new_dvert, gps->dvert, sizeof(MDeformVert) * num_points);
136
137                                 /* free unused point weights */
138                                 for (int i = num_points; i < gps->totpoints; i++) {
139                                         MDeformVert *dvert = &gps->dvert[i];
140                                         BKE_gpencil_free_point_weights(dvert);
141                                 }
142                         }
143                         break;
144                 }
145
146                 /* Hide in forward order = Remove points from start of stroke */
147                 case GP_BUILD_TRANSITION_FADE:
148                 {
149                         /* num_points is the number of points left after reducing.
150                          * We need to know how many to remove
151                          */
152                         const int offset = gps->totpoints - num_points;
153
154                         /* copy over point data */
155                         memcpy(new_points, gps->points + offset, sizeof(bGPDspoint) * num_points);
156                         if (gps->dvert != NULL) {
157                                 memcpy(new_dvert, gps->dvert + offset, sizeof(MDeformVert) * num_points);
158
159                                 /* free unused weights */
160                                 for (int i = 0; i < offset; i++) {
161                                         MDeformVert *dvert = &gps->dvert[i];
162                                         BKE_gpencil_free_point_weights(dvert);
163                                 }
164                         }
165                         break;
166                 }
167
168                 default:
169                         printf("ERROR: Unknown transition %d in %s()\n", (int)transition, __func__);
170                         break;
171         }
172
173         /* replace stroke geometry */
174         MEM_SAFE_FREE(gps->points);
175         MEM_SAFE_FREE(gps->dvert);
176         gps->points = new_points;
177         gps->dvert = new_dvert;
178         gps->totpoints = num_points;
179
180         /* mark stroke as needing to have its geometry caches rebuilt */
181         gps->flag |= GP_STROKE_RECALC_CACHES;
182         gps->tot_triangles = 0;
183         MEM_SAFE_FREE(gps->triangles);
184 }
185
186 /* --------------------------------------------- */
187
188 /* Stroke Data Table Entry - This represents one stroke being generated */
189 typedef struct tStrokeBuildDetails {
190         bGPDstroke *gps;
191
192         /* Indices - first/last indices for the stroke's points (overall) */
193         size_t start_idx, end_idx;
194
195         /* Number of points - Cache for more convenient access */
196         int totpoints;
197 } tStrokeBuildDetails;
198
199
200 /* Sequential - Show strokes one after the other */
201 static void build_sequential(BuildGpencilModifierData *mmd, bGPDframe *gpf, float fac)
202 {
203         const size_t tot_strokes = BLI_listbase_count(&gpf->strokes);
204         bGPDstroke *gps;
205         size_t i;
206
207         /* 1) Compute proportion of time each stroke should occupy */
208         /* NOTE: This assumes that the total number of points won't overflow! */
209         tStrokeBuildDetails *table = MEM_callocN(sizeof(tStrokeBuildDetails) * tot_strokes, __func__);
210         size_t totpoints = 0;
211
212         /* 1.1) First pass - Tally up points */
213         for (gps = gpf->strokes.first, i = 0; gps; gps = gps->next, i++) {
214                 tStrokeBuildDetails *cell = &table[i];
215
216                 cell->gps = gps;
217                 cell->totpoints = gps->totpoints;
218
219                 totpoints += cell->totpoints;
220         }
221
222         /* 1.2) Second pass - Compute the overall indices for points */
223         for (i = 0; i < tot_strokes; i++) {
224                 tStrokeBuildDetails *cell = &table[i];
225
226                 if (i == 0) {
227                         cell->start_idx = 0;
228                 }
229                 else {
230                         cell->start_idx = (cell - 1)->end_idx;
231                 }
232                 cell->end_idx = cell->start_idx + cell->totpoints - 1;
233         }
234
235
236         /* 2) Determine the global indices for points that should be visible */
237         size_t first_visible = 0;
238         size_t last_visible = 0;
239
240         switch (mmd->transition) {
241                 /* Show in forward order
242                  *  - As fac increases, the number of visible points increases
243                  */
244                 case GP_BUILD_TRANSITION_GROW:
245                         first_visible = 0; /* always visible */
246                         last_visible  = (size_t)roundf(totpoints * fac);
247                         break;
248
249                 /* Hide in reverse order
250                  *  - As fac increases, the number of points visible at the end decreases
251                  */
252                 case GP_BUILD_TRANSITION_SHRINK:
253                         first_visible = 0; /* always visible (until last point removed) */
254                         last_visible  = (size_t)(totpoints * (1.0f - fac));
255                         break;
256
257                 /* Hide in forward order
258                  *  - As fac increases, the early points start getting hidden
259                  */
260                 case GP_BUILD_TRANSITION_FADE:
261                         first_visible = (size_t)(totpoints * fac);
262                         last_visible  = totpoints; /* i.e. visible until the end, unless first overlaps this */
263                         break;
264         }
265
266
267         /* 3) Go through all strokes, deciding which to keep, and/or how much of each to keep */
268         for (i = 0; i < tot_strokes; i++) {
269                 tStrokeBuildDetails *cell = &table[i];
270
271                 /* Determine what portion of the stroke is visible */
272                 if ((cell->end_idx < first_visible) || (cell->start_idx > last_visible)) {
273                         /* Not visible at all - Either ended before */
274                         clear_stroke(gpf, cell->gps);
275                 }
276                 else {
277                         /* Some proportion of stroke is visible */
278                         /* XXX: Will the transition settings still be valid now? */
279                         if ((first_visible <= cell->start_idx) && (last_visible >= cell->end_idx)) {
280                                 /* Do nothing - whole stroke is visible */
281                         }
282                         else if (first_visible > cell->start_idx) {
283                                 /* Starts partway through this stroke */
284                                 int num_points = cell->end_idx - first_visible;
285                                 reduce_stroke_points(cell->gps, num_points, mmd->transition);
286                         }
287                         else {
288                                 /* Ends partway through this stroke */
289                                 int num_points = last_visible - cell->start_idx;
290                                 reduce_stroke_points(cell->gps, num_points, mmd->transition);
291                         }
292                 }
293         }
294
295         /* Free table */
296         MEM_freeN(table);
297 }
298
299 /* --------------------------------------------- */
300
301 /* Concurrent - Show multiple strokes at once */
302 // TODO: Allow random offsets to start times
303 // TODO: Allow varying speeds? Scaling of progress?
304 static void build_concurrent(BuildGpencilModifierData *mmd, bGPDframe *gpf, float fac)
305 {
306         bGPDstroke *gps, *gps_next;
307         int max_points = 0;
308
309         const bool reverse = (mmd->transition != GP_BUILD_TRANSITION_GROW);
310
311         /* 1) Determine the longest stroke, to figure out when short strokes should start */
312         /* FIXME: A *really* long stroke here could dwarf everything else, causing bad timings */
313         for (gps = gpf->strokes.first; gps; gps = gps->next) {
314                 if (gps->totpoints > max_points) {
315                         max_points = gps->totpoints;
316                 }
317         }
318         if (max_points == 0) {
319                 printf("ERROR: Strokes are all empty (GP Build Modifier: %s)\n", __func__);
320                 return;
321         }
322
323         /* 2) For each stroke, determine how it should be handled */
324         for (gps = gpf->strokes.first; gps; gps = gps_next) {
325                 gps_next = gps->next;
326
327                 /* Relative Length of Stroke - Relative to the longest stroke,
328                  * what proportion of the available time should this stroke use
329                  */
330                 const float relative_len = (float)gps->totpoints / (float)max_points;
331
332                 /* Determine how many points should be left in the stroke */
333                 int num_points = 0;
334
335                 switch (mmd->time_alignment) {
336                         case GP_BUILD_TIMEALIGN_START: /* all start on frame 1 */
337                         {
338                                 /* Build effect occurs over when fac = 0, to fac = relative_len */
339                                 if (fac <= relative_len) {
340                                         /* Scale fac to fit relative_len */
341                                         /* FIXME: prevent potential div by zero (e.g. very short stroke vs one very long one) */
342                                         const float scaled_fac = fac / relative_len;
343
344                                         if (reverse) {
345                                                 num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints);
346                                         }
347                                         else {
348                                                 num_points = (int)roundf(scaled_fac * gps->totpoints);
349                                         }
350                                 }
351                                 else {
352                                         /* Build effect has ended */
353                                         if (reverse) {
354                                                 num_points = 0;
355                                         }
356                                         else {
357                                                 num_points = gps->totpoints;
358                                         }
359                                 }
360
361                                 break;
362                         }
363                         case GP_BUILD_TIMEALIGN_END: /* all end on same frame */
364                         {
365                                 /* Build effect occurs over  1.0 - relative_len, to 1.0  (i.e. over the end of the range) */
366                                 const float start_fac = 1.0f - relative_len;
367
368                                 if (fac >= start_fac) {
369                                         /* FIXME: prevent potential div by zero (e.g. very short stroke vs one very long one) */
370                                         const float scaled_fac = (fac - start_fac) / relative_len;
371
372                                         if (reverse) {
373                                                 num_points = (int)roundf((1.0f - scaled_fac) * gps->totpoints);
374                                         }
375                                         else {
376                                                 num_points = (int)roundf(scaled_fac * gps->totpoints);
377                                         }
378                                 }
379                                 else {
380                                         /* Build effect hasn't started */
381                                         if (reverse) {
382                                                 num_points = gps->totpoints;
383                                         }
384                                         else {
385                                                 num_points = 0;
386                                         }
387                                 }
388
389                                 break;
390                         }
391
392                         /* TODO... */
393                 }
394
395                 /* Modify the stroke geometry */
396                 if (num_points <= 0) {
397                         /* Nothing Left - Delete the stroke */
398                         clear_stroke(gpf, gps);
399                 }
400                 else if (num_points < gps->totpoints) {
401                         /* Remove some points */
402                         reduce_stroke_points(gps, num_points, mmd->transition);
403                 }
404         }
405 }
406
407 /* --------------------------------------------- */
408
409 /* Entry-point for Build Modifier */
410 static void generateStrokes(
411         GpencilModifierData *md, Depsgraph *depsgraph,
412         Object *UNUSED(ob), bGPDlayer *gpl, bGPDframe *gpf)
413 {
414         BuildGpencilModifierData *mmd = (BuildGpencilModifierData *)md;
415         const bool reverse = (mmd->transition != GP_BUILD_TRANSITION_GROW);
416
417         const float ctime = DEG_get_ctime(depsgraph);
418         //printf("GP Build Modifier - %f\n", ctime);
419
420         /* Early exit if it's an empty frame */
421         if (gpf->strokes.first == NULL) {
422                 return;
423         }
424
425         /* Omit layer if filter by layer */
426         if (mmd->layername[0] != '\0') {
427                 if ((mmd->flag & GP_BUILD_INVERT_LAYER) == 0) {
428                         if (!STREQ(mmd->layername, gpl->info)) {
429                                 return;
430                         }
431                 }
432                 else {
433                         if (STREQ(mmd->layername, gpl->info)) {
434                                 return;
435                         }
436                 }
437         }
438         /* verify layer pass */
439         if (mmd->layer_pass > 0) {
440                 if ((mmd->flag & GP_BUILD_INVERT_LAYERPASS) == 0) {
441                         if (gpl->pass_index != mmd->layer_pass) {
442                                 return;
443                         }
444                 }
445                 else {
446                         if (gpl->pass_index == mmd->layer_pass) {
447                                 return;
448                         }
449                 }
450         }
451
452         /* Early exit if outside of the frame range for this modifier
453          * (e.g. to have one forward, and one backwards modifier)
454          */
455         if (mmd->flag & GP_BUILD_RESTRICT_TIME) {
456                 if ((ctime < mmd->start_frame) || (ctime > mmd->end_frame)) {
457                         return;
458                 }
459         }
460
461         /* Compute start and end frames for the animation effect
462          * By default, the upper bound is given by the "maximum length" setting
463          */
464         float start_frame = gpf->framenum + mmd->start_delay;
465         float end_frame   = gpf->framenum + mmd->length;
466
467         if (gpf->next) {
468                 /* Use the next frame or upper bound as end frame, whichever is lower/closer */
469                 end_frame = MIN2(end_frame, gpf->next->framenum);
470         }
471
472
473         /* Early exit if current frame is outside start/end bounds */
474         /* NOTE: If we're beyond the next/prev frames (if existent), then we wouldn't have this problem anyway... */
475         if (ctime < start_frame) {
476                 /* Before Start - Animation hasn't started. Display initial state. */
477                 if (reverse) {
478                         /* 1) Reverse = Start with all, end with nothing.
479                          *    ==> Do nothing (everything already present)
480                          */
481                 }
482                 else {
483                         /* 2) Forward Order = Start with nothing, end with the full frame.
484                          *    ==> Free all strokes, and return an empty frame
485                          */
486                         gpf_clear_all_strokes(gpf);
487                 }
488
489                 /* Early exit */
490                 return;
491         }
492         else if (ctime >= end_frame) {
493                 /* Past End - Animation finished. Display final result. */
494                 if (reverse) {
495                         /* 1) Reverse = Start with all, end with nothing.
496                          *    ==> Free all strokes, and return an empty frame
497                          */
498                         gpf_clear_all_strokes(gpf);
499                 }
500                 else {
501                         /* 2) Forward Order = Start with nothing, end with the full frame.
502                          *    ==> Do Nothing (everything already present)
503                          */
504                 }
505
506                 /* Early exit */
507                 return;
508         }
509
510
511         /* Determine how far along we are between the keyframes */
512         float fac = (ctime - start_frame) / (end_frame - start_frame);
513         //printf("  Progress on %d = %f (%f - %f)\n", gpf->framenum, fac, start_frame, end_frame);
514
515         /* Time management mode */
516         switch (mmd->mode) {
517                 case GP_BUILD_MODE_SEQUENTIAL:
518                         build_sequential(mmd, gpf, fac);
519                         break;
520
521                 case GP_BUILD_MODE_CONCURRENT:
522                         build_concurrent(mmd, gpf, fac);
523                         break;
524
525                 default:
526                         printf("Unsupported build mode (%d) for GP Build Modifier: '%s'\n", mmd->mode, mmd->modifier.name);
527                         break;
528         }
529 }
530
531
532 /* ******************************************** */
533
534 GpencilModifierTypeInfo modifierType_Gpencil_Build = {
535         /* name */              "Build",
536         /* structName */        "BuildGpencilModifierData",
537         /* structSize */        sizeof(BuildGpencilModifierData),
538         /* type */              eGpencilModifierTypeType_Gpencil,
539         /* flags */             eGpencilModifierTypeFlag_NoApply,
540
541         /* copyData */          copyData,
542
543         /* deformStroke */      NULL,
544         /* generateStrokes */   generateStrokes,
545         /* bakeModifier */      NULL,
546         /* remapTime */         NULL,
547
548         /* initData */          initData,
549         /* freeData */          NULL,
550         /* isDisabled */        NULL,
551         /* updateDepsgraph */   NULL,
552         /* dependsOnTime */     dependsOnTime,
553         /* foreachObjectLink */ NULL,
554         /* foreachIDLink */     NULL,
555         /* foreachTexLink */    NULL,
556         /* getDuplicationFactor */ NULL,
557 };