[Bf-blender-cvs] [47c9dda0761] greasepencil-object: GP Build Modifier: Initial WIP implementation of this modifier

Joshua Leung noreply at git.blender.org
Thu Nov 23 15:33:01 CET 2017


Commit: 47c9dda076183ab6019303bf47075cff75336cfd
Author: Joshua Leung
Date:   Thu Nov 23 17:31:49 2017 +1300
Branches: greasepencil-object
https://developer.blender.org/rB47c9dda076183ab6019303bf47075cff75336cfd

GP Build Modifier: Initial WIP implementation of this modifier

It's likely that there are still many bugs here, so caution is advised.

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

M	source/blender/makesdna/DNA_modifier_types.h
M	source/blender/modifiers/intern/MOD_gpencilbuild.c

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

diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 83d36bc9c5d..0ed34224af2 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -1769,14 +1769,57 @@ typedef enum eGpencilArray_Flag {
 
 typedef struct GpencilBuildModifierData {
 	ModifierData modifier;
-	float start, length;
-	int flag;
-	int seed;             /* (int) random seed */
+	
+	char layername[64];   /* if set, restrict modifier to operating on this layer */
+	
+	float start_frame;    /* If GP_BUILD_RESTRICT_TIME is set, the defines the frame range where GP frames are considered */
+	float end_frame;
+	
+	/* TODO: color (paletteslot + colorname) used for appearance of stroke tips being drawn */
+	
+	float start_delay;    /* For each pair of gp keys, number of frames before strokes start appearing */
+	float length;         /* For each pair of gp keys, number of frames that build effect must be completed within */
+	
+	short flag;           /* (eGpencilBuild_Flag) Options for controlling modifier behaviour */
+	
+	short mode;           /* (eGpencilBuild_Mode) How are strokes ordered */
+	short direction;      /* (eGpencilBuild_Direction) In what order do stroke points appear/disappear */
+	
+	short time_alignment; /* (eGpencilBuild_TimeAlignment) For the "Concurrent" mode, when should "shorter" strips start/end */
 } GpencilBuildModifierData;
 
+typedef enum eGpencilBuild_Mode {
+	/* Strokes are shown one by one until all have appeared */
+	GP_BUILD_MODE_SEQUENTIAL = 0,
+	/* All strokes start at the same time */
+	GP_BUILD_MODE_CONCURRENT = 1,
+} eGpencilBuild_Mode;
+
+typedef enum eGpencilBuild_Direction {
+	/* Show in forward order */
+	GP_BUILD_DIRECTION_GROW    = 0,
+	/* Hide in reverse order */
+	GP_BUILD_DIRECTION_SHRINK  = 1,
+	/* Hide in forward order */
+	GP_BUILD_DIRECTION_FADE    = 2,
+} eGpencilBuild_Direction;
+
+typedef enum eGpencilBuild_TimeAlignment {
+	/* All strokes start at same time */
+	GP_BUILD_TIMEALIGN_START = 0,
+	/* All strokes end at same time */
+	GP_BUILD_TIMEALIGN_END   = 1,
+	
+	/* TODO: Random Offsets, Stretch-to-Fill */
+} eGpencilBuild_TimeAlignment;
+
 typedef enum eGpencilBuild_Flag {
-	GP_BUILD_FLAG_RANDOMIZE = (1 << 0),  /* order of vertices is randomized */
-	GP_BUILD_FLAG_REVERSE   = (1 << 1),  /* frame range is reversed, resulting in a deconstruction effect */
+	/* Restrict modifier to particular layer/passes? */
+	GP_BUILD_INVERSE_LAYER  = (1 << 0),
+	GP_BUILD_INVERSE_PASS   = (1 << 1),
+	
+	/* Restrict modifier to only operating between the nominated frames */
+	GP_BUILD_RESTRICT_TIME  = (1 << 2),
 } eGpencilBuild_Flag;
 
 
diff --git a/source/blender/modifiers/intern/MOD_gpencilbuild.c b/source/blender/modifiers/intern/MOD_gpencilbuild.c
index 94f3f1ad378..6abea33479c 100644
--- a/source/blender/modifiers/intern/MOD_gpencilbuild.c
+++ b/source/blender/modifiers/intern/MOD_gpencilbuild.c
@@ -30,11 +30,16 @@
 
 #include <stdio.h>
 
+#include "MEM_guardedalloc.h"
+
 #include "DNA_scene_types.h"
 #include "DNA_object_types.h"
 #include "DNA_gpencil_types.h"
 
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
 #include "BLI_utildefines.h"
+
 #include "BKE_context.h"
 #include "BKE_gpencil.h"
 
@@ -45,7 +50,17 @@
 
 static void initData(ModifierData *md)
 {
-	//GpencilBuildModifierData *gpmd = (GpencilBuildModifierData *)md;
+	GpencilBuildModifierData *gpmd = (GpencilBuildModifierData *)md;
+	
+	/* We deliberately set this range to the half the default
+	 * frame-range to have an immediate effect ot suggest use-cases
+	 */
+	gpmd->start_frame = 1;
+	gpmd->end_frame = 125;
+	
+	/* Init default length of each build effect - Nothing special */
+	gpmd->start_delay = 0.0f;
+	gpmd->length = 100.0f;
 }
 
 static void copyData(ModifierData *md, ModifierData *target)
@@ -53,13 +68,436 @@ static void copyData(ModifierData *md, ModifierData *target)
 	modifier_copyData_generic(md, target);
 }
 
+static bool dependsOnTime(ModifierData *UNUSED(md))
+{
+	return true;
+}
+
+/* ******************************************** */
+/* Build Modifier - Stroke generation logic
+ *
+ * There are two modes for how the strokes are sequenced (at a macro-level):
+ * - Sequential Mode - Strokes appear/disappear one after the other. Only a single one changes at a time.
+ * - Concurrent Mode - Multiple strokes appear/disappear at once.
+ *
+ * Assumptions:
+ * - Stroke points are generally equally spaced. This implies that we can just add/remove points,
+ *   without worrying about distances between them / adding extra interpolated points between
+ *   an visible point and one about to be added/removed (or any similar tapering effects).
+ 
+ * - All strokes present are fully visible (i.e. we don't have to ignore any)
+ */
+
+/* Remove a particular stroke, freeing the palcolor too, since it is a local copy */
+static void clear_stroke(bGPDstroke *gps, int modifier_index)
+{
+	if (modifier_index > -1) {
+		/* This is part of the modifier stack, meaning that we're working on
+		 * "derived data" with local copies of the palcolors that we need to free
+		 */
+		MEM_SAFE_FREE(gps->palcolor);
+	}
+	
+	BKE_gpencil_free_stroke(gps);
+}
+
+/* Clear all strokes in frame */
+static void gpf_clear_all_strokes(bGPDframe *gpf, int modifier_index)
+{
+	bGPDstroke *gps, *gps_next;
+	for (gps = gpf->strokes.first; gps; gps = gps_next) {
+		gps_next = gps->next;
+		clear_stroke(gps, modifier_index);
+	}
+	BLI_listbase_clear(&gpf->strokes);
+}
+
+/* Reduce the number of points in the stroke
+ *
+ * Note: This won't be called if all points are present/removed
+ * TODO: Allow blending of growing/shrinking tip (e.g. for more gradual transitions)
+ */
+static void reduce_stroke_points(bGPDstroke *gps, const int num_points, const eGpencilBuild_Direction direction)
+{
+	bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * num_points, "GP Build Modifier - Reduced Points");
+	
+	/* Which end should points be removed from */
+	// TODO: free stroke weights
+	switch (direction) {
+		case GP_BUILD_DIRECTION_GROW:   /* Show in forward order = Remove ungrown-points from end of stroke */
+		case GP_BUILD_DIRECTION_SHRINK: /* Hide in reverse order = Remove dead-points from end of stroke */
+		{
+			/* copy over point data */
+			memcpy(new_points, gps->points, sizeof(bGPDspoint) * num_points);
+			
+			/* free unused point weights */
+			for (int i = num_points; i < gps->totpoints; i++) {
+				bGPDspoint *pt = &gps->points[i];
+				BKE_gpencil_free_point_weights(pt);
+			}
+			
+			break;
+		}
+		
+		/* Hide in forward order = Remove points from start of stroke */
+		case GP_BUILD_DIRECTION_FADE:
+		{
+			/* num_points is the number of points left after reducing.
+			 * We need to know how many to remove
+			 */
+			const int offset = gps->totpoints - num_points;
+			
+			/* copy over point data */
+			memcpy(new_points, gps->points + offset, sizeof(bGPDspoint) * num_points);
+			
+			/* free unused point weights */
+			for (int i = 0; i < offset; i++) {
+				bGPDspoint *pt = &gps->points[i];
+				BKE_gpencil_free_point_weights(pt);
+			}
+			
+			break;
+		}
+		
+		default:
+			printf("ERROR: Unknown direction %d in %s()\n", direction, __func__);
+			break;
+	}
+	
+	/* replace stroke geometry */
+	MEM_freeN(gps->points);
+	gps->points = new_points;
+	gps->totpoints = num_points;
+	
+	/* mark stroke as needing to have its geometry caches rebuilt */
+	gps->flag |= GP_STROKE_RECALC_CACHES;
+	gps->tot_triangles = 0;
+}
+
+/* --------------------------------------------- */
+
+/* Stroke Data Table Entry - This represents one stroke being generated */
+typedef struct tStrokeBuildDetails {
+	bGPDstroke *gps;
+	
+	/* Indices - first/last indices for the stroke's points (overall) */
+	size_t start_idx, end_idx;
+	
+	/* Number of points - Cache for more convenient access */
+	int totpoints;
+} tStrokeBuildDetails;
+
+
+/* Sequential - Show strokes one after the other */
+static void build_sequential(GpencilBuildModifierData *mmd, bGPDlayer *gpl, bGPDframe *gpf, float fac, int modifier_index)
+{
+	const size_t tot_strokes = BLI_listbase_count(&gpf->strokes);
+	bGPDstroke *gps;
+	size_t i;
+	
+	/* 1) 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, "tStrokeBuildDetails");
+	size_t totpoints = 0;
+	
+	/* 1.1) First pass - Tally up points */
+	for (gps = gpf->strokes.first, i = 0; gps; gps = gps->next, i++) {
+		tStrokeBuildDetails *cell = &table[i];
+		
+		cell->gps = gps;
+		cell->totpoints = gps->totpoints;
+	}
+	
+	/* 1.2) Second pass - Compute the overall indices for points */
+	for (i = 0; i < tot_strokes; i++) {
+		tStrokeBuildDetails *cell = &table[i];
+		
+		if (i == 0) {
+			cell->start_idx = 0;
+		}
+		else {
+			cell->start_idx = (cell - 1)->end_idx;
+		}
+		cell->end_idx = cell->start_idx + cell->totpoints - 1;
+	}
+	
+	
+	/* 2) Determine the global indices for points that should be visible */
+	size_t first_visible = 0;
+	size_t last_visible = 0;
+	
+	switch (mmd->direction) {
+		/* Show in forward order
+		 *  - As fac increases, the number of visible points increases
+		 */
+		case GP_BUILD_DIRECTION_GROW:
+			first_visible = 0; /* always visible */
+			last_visible  = (size_t)roundf(totpoints * fac);
+			break;
+			
+		/* Hide in reverse order
+		 *  - As fac increases, the number of points visible at the end decreases
+		 */
+		case GP_BUILD_DIRECTION_SHRINK:
+			first_visible = 0; /* always visible (until last point removed) */
+			last_visible  = (size_t)(totpoints * (1.0f - fac));
+			break;
+		
+		/* Hide in forward order
+		 *  - As fac increases, the early points start getting hidden 
+		 */
+		case GP_BUILD_DIRECTION_FADE:
+			first_visible = (size_t)(totpoints * fac);
+			last_visible  = totpoints; /* i.e. visible until the end, unless first overlaps this */
+			break;
+	}
+	
+	
+	/* 3) Go through all strokes, deciding which to keep, and/or how much of each to keep */
+	for (i = 0; i < tot_strokes; i++) {
+		tStrokeBuildDetails *cell = &table[i];
+		
+		/* Determine what portion of the stroke is visible */
+		if (

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list