Gah... Forgot to thanks Aligorith for his review work, sorry! On 11/11/2012 15:48, Bastien Montagne wrote: > Revision: 52096 > > http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=52096 > Author: mont29 > Date: 2012-11-11 14:48:58 +0000 (Sun, 11 Nov 2012) > Log Message: > ----------- > "Dynamic Sketch" patch, which adds timing data to GP strokes, by storing an > inittime in each stroke (value returned by PIL_check_seconds_timer() func), > and then a delta time for each of its points, relative to that inittime. > > These timing data can then be used during conversion to Curve objects, to > create a path animation (i.e. an Evaluation Time F-Curve) exactly reproducing > the drawing movements. > > Aside from this "main feature", the patch brings several fixes/enhancements: > * Stroke smoothing/simplifying will no more move the start/end points of a > stroke (this was rather annoying sometimes!). > * Also optimized smoothing code (even though not really noticeable on a > modern computer, it now uses less memory and runs faster). > * When converting to curve, you now have the following new possibilities: > ** Normalize the weight values (currently, they will get "stroke width * > 0.1", i.e. would range by default from 0.0 to 0.3...). > ** Scale the radius values to your liking (again, currently they are set from > stroke width times 0.1)! > ** Link all strokes into a single curve, using zero-radius sections (this is > mandatory to use the dynamic feature!). > > Here is a small demo video: http://youtu.be/VwWEXrnQAFI > > Will update user manual later today. > > Modified Paths: > -------------- > trunk/blender/source/blender/editors/gpencil/gpencil_edit.c > trunk/blender/source/blender/editors/gpencil/gpencil_paint.c > trunk/blender/source/blender/editors/include/ED_gpencil.h > trunk/blender/source/blender/makesdna/DNA_gpencil_types.h > > Modified: trunk/blender/source/blender/editors/gpencil/gpencil_edit.c > =================================================================== > --- trunk/blender/source/blender/editors/gpencil/gpencil_edit.c > 2012-11-11 14:33:06 UTC (rev 52095) > +++ trunk/blender/source/blender/editors/gpencil/gpencil_edit.c > 2012-11-11 14:48:58 UTC (rev 52096) > @@ -40,8 +40,10 @@ > > #include "BLI_math.h" > #include "BLI_blenlib.h" > +#include "BLI_rand.h" > #include "BLI_utildefines.h" > > +#include "DNA_anim_types.h" > #include "DNA_curve_types.h" > #include "DNA_object_types.h" > #include "DNA_node_types.h" > @@ -51,14 +53,20 @@ > #include "DNA_view3d_types.h" > #include "DNA_gpencil_types.h" > > +#include "BKE_animsys.h" > #include "BKE_context.h" > #include "BKE_curve.h" > +#include "BKE_depsgraph.h" > +#include "BKE_fcurve.h" > +#include "BKE_global.h" > #include "BKE_gpencil.h" > #include "BKE_library.h" > +#include "BKE_main.h" > #include "BKE_object.h" > #include "BKE_report.h" > #include "BKE_tracking.h" > > +#include "UI_interface.h" > > #include "WM_api.h" > #include "WM_types.h" > @@ -71,6 +79,7 @@ > #include "ED_gpencil.h" > #include "ED_view3d.h" > #include "ED_clip.h" > +#include "ED_keyframing.h" > > #include "gpencil_intern.h" > > @@ -387,6 +396,14 @@ > GP_STROKECONVERT_CURVE, > }; > > +/* Defines for possible timing modes */ > +enum { > + GP_STROKECONVERT_TIMING_NONE = 1, > + GP_STROKECONVERT_TIMING_LINEAR = 2, > + GP_STROKECONVERT_TIMING_FULL = 3, > + GP_STROKECONVERT_TIMING_CUSTOMGAP = 4, > +}; > + > /* RNA enum define */ > static EnumPropertyItem prop_gpencil_convertmodes[] = { > {GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""}, > @@ -394,6 +411,31 @@ > {0, NULL, 0, NULL, NULL} > }; > > +static EnumPropertyItem prop_gpencil_convert_timingmodes_restricted[] = { > + {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"}, > + {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear > timing"}, > + {0, NULL, 0, NULL, NULL}, > +}; > + > +static EnumPropertyItem prop_gpencil_convert_timingmodes[] = { > + {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"}, > + {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear > timing"}, > + {GP_STROKECONVERT_TIMING_FULL, "FULL", 0, "Original", "Use the original > timing, gaps included"}, > + {GP_STROKECONVERT_TIMING_CUSTOMGAP, "CUSTOMGAP", 0, "Custom Gaps", > + "Use the original timing, but with > custom gap lengths (in frames)"}, > + {0, NULL, 0, NULL, NULL}, > +}; > + > +static EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), > PointerRNA *ptr, PropertyRNA *UNUSED(prop), > + int *free) > +{ > + *free = FALSE; > + if (RNA_boolean_get(ptr, "use_timing_data")) { > + return prop_gpencil_convert_timingmodes; > + } > + return prop_gpencil_convert_timingmodes_restricted; > +} > + > /* --- */ > > /* convert the coordinates from the given stroke point into 3d-coordinates > @@ -440,40 +482,485 @@ > > /* --- */ > > +/* temp struct for gp_stroke_path_animation() */ > +typedef struct tGpTimingData { > + /* Data set from operator settings */ > + int mode; > + int frame_range; /* Number of frames evaluated for path animation */ > + int start_frame, end_frame; > + int realtime; /* A bool, actually, will overwrite end_frame in case of > Original or CustomGap timing... */ > + float gap_duration, gap_randomness; /* To be used with CustomGap mode*/ > + int seed; > + > + /* Data set from points, used to compute final timing FCurve */ > + int num_points, cur_point; > + > + /* Distances */ > + float *dists; > + float tot_dist; > + > + /* Times */ > + float *times; /* Note: Gap times will be negative! */ > + float tot_time, gap_tot_time; > + double inittime; > +} tGpTimingData; > + > +static void _gp_timing_data_set_nbr(tGpTimingData *gtd, int nbr) > +{ > + float *tmp; > + > + BLI_assert(nbr> gtd->num_points); > + > + tmp = gtd->dists; > + gtd->dists = MEM_callocN(sizeof(float) * nbr, __func__); > + if (tmp) { > + memcpy(gtd->dists, tmp, sizeof(float) * gtd->num_points); > + MEM_freeN(tmp); > + } > + > + tmp = gtd->times; > + gtd->times = MEM_callocN(sizeof(float) * nbr, __func__); > + if (tmp) { > + memcpy(gtd->times, tmp, sizeof(float) * gtd->num_points); > + MEM_freeN(tmp); > + } > + > + gtd->num_points = nbr; > +} > + > +static void gp_timing_data_add_point(tGpTimingData *gtd, double > stroke_inittime, float time, float delta_dist) > +{ > + if (time< 0.0f) { > + /* This is a gap, negative value! */ > + gtd->tot_time = -(gtd->times[gtd->cur_point] = > -(((float)(stroke_inittime - gtd->inittime)) + time)); > + gtd->gap_tot_time += gtd->times[gtd->cur_point] - > gtd->times[gtd->cur_point - 1]; > + } > + else > + gtd->tot_time = (gtd->times[gtd->cur_point] = > (((float)(stroke_inittime - gtd->inittime)) + time)); > + gtd->dists[gtd->cur_point] = (gtd->tot_dist += delta_dist); > + gtd->cur_point++; > +} > + > +/* In frames! Binary search for FCurve keys have a threshold of 0.01, so we > can’t set > + * arbitrarily close points - this is esp. important with NoGaps mode! > + */ > +#define MIN_TIME_DELTA 0.02f > + > +/* Loop over next points to find the end of the stroke, and compute */ > +static int gp_find_end_of_stroke_idx(tGpTimingData *gtd, int idx, int > nbr_gaps, int *nbr_done_gaps, > + float tot_gaps_time, float delta_time, > float *next_delta_time) > +{ > + int j; > + > + for (j = idx + 1; j< gtd->num_points; j++) { > + if (gtd->times[j]< 0) { > + gtd->times[j] = -gtd->times[j]; > + if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { > + /* In this mode, gap time between this stroke > and the next should be 0 currently... > + * So we have to compute its final duration! > + */ > + if (gtd->gap_randomness> 0.0f) { > + /* We want gaps that are in > gtd->gap_duration +/- gtd->gap_randomness range, > + * and which sum to exactly > tot_gaps_time... > + */ > + int rem_gaps = nbr_gaps - > *nbr_done_gaps; > + if (rem_gaps< 2) { > + /* Last gap, just give > remaining time! */ > + *next_delta_time = > tot_gaps_time; > + } > + else { > + float delta, min, max; > + /* This code ensures that if > the first gaps have been shorter than average gap_duration, > + * next gaps will tend to be > longer (i.e. try to recover the lateness), and vice-versa! > + */ > + delta = delta_time - > (gtd->gap_duration * *nbr_done_gaps); > + /* Clamp min between > [-gap_randomness, 0.0], with lower delta giving higher min */ > + min = -gtd->gap_randomness - > delta; > + CLAMP(min, > -gtd->gap_randomness, 0.0f); > + /* Clamp max between [0.0, > gap_randomness], with lower delta giving higher max */ > + max = gtd->gap_randomness - > delta; > + CLAMP(max, 0.0f, > gtd->gap_randomness); > + *next_delta_time += > gtd->gap_duration + (BLI_frand() * (max - min)) + min; > + } > + } > + else { > + *next_delta_time += gtd->gap_duration; > + } > + } > + (*nbr_done_gaps)++; > + break; > + } > + } > + > + return j - 1; > +} > + > +static void gp_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, int > *nbr_gaps, float *tot_gaps_time) > +{ > + int i; > + float delta_time = 0.0f; > + > + for (i = 0; i< gtd->num_points; i++) { > + if (gtd->times[i]< 0&& i) { > + (*nbr_gaps)++; > + gtd->times[i] = -gtd->times[i] - delta_time; > + delta_time += gtd->times[i] - gtd->times[i - 1]; > + gtd->times[i] = -gtd->times[i - 1]; /* Temp marker, > values *have* to be different! */ > + } > + else { > + gtd->times[i] -= delta_time; > + } > + } > + gtd->tot_time -= delta_time; > + > + *tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration; > + gtd->tot_time += *tot_gaps_time; > + if (G.debug& G_DEBUG) { > + printf("%f, %f, %f, %d\n", gtd->tot_time, delta_time, > *tot_gaps_time, *nbr_gaps); > + } > + if (gtd->gap_randomness> 0.0f) { > + BLI_srandom(gtd->seed); > + } > +} > + > +static void gp_stroke_path_animation_add_keyframes(ReportList *reports, > PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, > + Curve *cu, tGpTimingData > *gtd, float cfra, float time_range, > + int nbr_gaps, float > tot_gaps_time) > +{ > + /* Use actual recorded timing! */ > + float time_start = (float)gtd->start_frame; > + > + float last_valid_time = 0.0f; > + int end_stroke_idx = -1, start_stroke_idx = 0; > + float end_stroke_time = 0.0f; > + > + /* CustomGaps specific */ > + float delta_time = 0.0f, next_delta_time = 0.0f; > + int nbr_done_gaps = 0; > + > + int i; > + > + /* This is a bit tricky, as: > + * - We can't add arbitrarily close points on FCurve (in time). > + * - We *must* have all "caps" points of all strokes in FCurve, as much > as possible! > + */ > + for (i = 0; i< gtd->num_points; i++) { > + /* If new stroke... */ > + if (i> end_stroke_idx) { > + start_stroke_idx = i; > + delta_time = next_delta_time; > + /* find end of that new stroke */ > + end_stroke_idx = gp_find_end_of_stroke_idx(gtd, i, > nbr_gaps,&nbr_done_gaps, > + > tot_gaps_time, delta_time,&next_delta_time); > + /* This one should *never* be negative! */ > + end_stroke_time = time_start + > ((gtd->times[end_stroke_idx] + delta_time) / gtd->tot_time * time_range); > + } > + > + /* Simple proportional stuff... */ > + cu->ctime = gtd->dists[i] / gtd->tot_dist * cu->pathlen; > + cfra = time_start + ((gtd->times[i] + delta_time) / > gtd->tot_time * time_range); > + > + /* And now, the checks about timing... */ > + if (i == start_stroke_idx) { > + /* If first point of a stroke, be sure it's enough > ahead of last valid keyframe, and > + * that the end point of the stroke is far enough! > + * In case it is not, we keep the end point... > + * Note that with CustomGaps mode, this is here we set > the actual gap timing! > + */ > + if ((end_stroke_time - last_valid_time)> > MIN_TIME_DELTA * 2) { > + if ((cfra - last_valid_time)< MIN_TIME_DELTA) { > + cfra = last_valid_time + MIN_TIME_DELTA; > + } > + insert_keyframe_direct(reports, ptr, prop, fcu, > cfra, INSERTKEY_FAST); > + last_valid_time = cfra; > + } > + else if (G.debug& G_DEBUG) { > + printf("\t Skipping start point %d, too close > from end point %d\n", i, end_stroke_idx); > + } > + } > + else if (i == end_stroke_idx) { > + /* Always try to insert end point of a curve (should be > safe enough, anyway...) */ > + if ((cfra - last_valid_time)< MIN_TIME_DELTA) { > + cfra = last_valid_time + MIN_TIME_DELTA; > + } > + insert_keyframe_direct(reports, ptr, prop, fcu, cfra, > INSERTKEY_FAST); > + last_valid_time = cfra; > + } > + else { > + /* Else ("middle" point), we only insert it if it's far > enough from last keyframe, > + * and also far enough from (not yet added!) end_stroke > keyframe! > + */ > + if ((cfra - last_valid_time)> MIN_TIME_DELTA&& > (end_stroke_time - cfra)> MIN_TIME_DELTA) { > > @@ Diff output truncated at 10240 characters. @@ > _______________________________________________ > Bf-blender-cvs mailing list > [email protected] > http://lists.blender.org/mailman/listinfo/bf-blender-cvs
_______________________________________________ Bf-committers mailing list [email protected] http://lists.blender.org/mailman/listinfo/bf-committers
